eslint-config-clean-typescript
Enforce classic JavaScript features in TypeScript codebase by banning excessive keywords
Though TypeScript has brought us type-safety for web development, many developers agree on that it has too many duplicated keywords and ways of writing code to achieve the same thing.
Not only that, TypeScript is not a standard web language. It might be hard to refactor the code once alternatives such as the type annotation proposal for JavaScript arise, if the codebase contains too much TypeScript-specific syntax.
This package is an opinionated ESLint configuration. The basic principle is not to declare things that don't exist in JavaScript. By doing so, TypeScript can be more coherent with JavaScript, while assisting only in areas that are related to type safety.
Table of Contents
Installation
This guide assumes that you're inside your Node project directory with package.json
.
Additionally, this configuration relies on eslint
and typescript-eslint
. You should have .eslintrc.js
or similar in your project folder, by following the exact steps introduced in the eslint
docs and typescript-eslint
docs.
Once the prerequisites are ready, install this configuration.
npm install --save-dev eslint-config-clean-typescript
Then, add this configuration to .eslintrc.js
or similar.
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/stylistic",
"eslint-config-clean-typescript" // ADD THIS LINE
]
}
Rules
Class Based Types
- ⭕
class
- ❌
interface
Whether to use type
, interface
, or class
to represent a typed structure has been a long-standing controversy in the TypeScript community.
This is related to the historical path of TypeScript, where the interface
keyword was introduced in 2012, when JavaScript class
keyword was not a thing yet. The JavaScript class
keyword was introduced later in 2015.
This configuration makes your codebase use only class
for types, to adhere to classic JavaScript way of doing object-oriented programming.
// Produces warning
enum MyEnum {
A = "west",
B = "east",
}
// Produces warning
interface MyInterface {
a: number;
b: string;
}
// OK
class MyEnum {
static A = "west";
static B = "east";
}
// OK
class MyInterface {
a: number;
b: string;
}
Consider using optional types such as number | undefined
for fields that can be empty or extended. This is equivalent to TypeScript's Omit
or Pick
.
// Recommended
class MyClass {
a?: number; // number | undefined
b?: string; // string | undefined
}
No Type Aliases
- ❌
type
Type statements are ghost declarations that don't actually exist in JavaScript.
// Produces warning
type ShapeType = { a: boolean; b: boolean };
// Produces warning
type AliasType = Array<string>;
// Not recommended
function myFunction(array: AliasType) {
console.log(array);
}
// Recommended
function myFunction(array: Array<string>) {
console.log(array);
}
No Namespaces
- ❌
namespace
ES6 modules should be used instead of namespaces. TypeScript was heavily influced by C++ and C#, and the namespace
keyword doesn't align well with JavaScript practices. This rule is also included in typescript-eslint
's recommended configuration.
// Produces warning
namespace MyNamespace {}
Array Type Annotations
- ⭕
Array<T>
- ❌
T[]
Preferring Array<T>
over T[]
is for ensuring alignment with high-level language paradigm, particularly in the context of JavaScript development.
Although T[]
may be more familiar to developers from lower-level languages like C, C++, and Rust, where arrays represent contiguous memory spaces, it does not really make sense for high-level languages like JavaScript.
In contrast, Array<T>
provides a more explicit and descriptive representation of the Array
type in JavaScript. This clarity is particularly beneficial for developers collaborating on projects, as it reduces ambiguity and aids in understanding the constructor and prototype of an array.
// Produces warning
const array: number[] = [1, 2, 3];
// OK
const array: Array<number> = [1, 2, 3];