Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin): added new rule typedef #581

Merged
merged 11 commits into from Jul 24, 2019
1 change: 1 addition & 0 deletions packages/eslint-plugin/README.md
Expand Up @@ -180,6 +180,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e
| [`@typescript-eslint/strict-boolean-expressions`](./docs/rules/strict-boolean-expressions.md) | Restricts the types allowed in boolean expressions | | | :thought_balloon: |
| [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Sets preference level for triple slash directives versus ES6-style import declarations | | | |
| [`@typescript-eslint/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) | Require consistent spacing around type annotations | :heavy_check_mark: | :wrench: | |
| [`@typescript-eslint/typedef`](./docs/rules/typedef.md) | Requires type annotations to exist | | | |
| [`@typescript-eslint/unbound-method`](./docs/rules/unbound-method.md) | Enforces unbound methods are called with their expected scope | | | :thought_balloon: |
| [`@typescript-eslint/unified-signatures`](./docs/rules/unified-signatures.md) | Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter | | | |

Expand Down
3 changes: 2 additions & 1 deletion packages/eslint-plugin/ROADMAP.md
Expand Up @@ -32,8 +32,8 @@
| [`only-arrow-functions`] | 🔌 | [`prefer-arrow/prefer-arrow-functions`] |
| [`prefer-for-of`] | ✅ | [`@typescript-eslint/prefer-for-of`] |
| [`promise-function-async`] | ✅ | [`@typescript-eslint/promise-function-async`] |
| [`typedef`] | 🛑 | N/A |
| [`typedef-whitespace`] | ✅ | [`@typescript-eslint/type-annotation-spacing`] |
| [`typedef`] | ✅ | [`@typescript-eslint/typedef`] |
| [`unified-signatures`] | ✅ | [`@typescript-eslint/unified-signatures`] |

<sup>[1]</sup> The ESLint rule only supports exact string matching, rather than regular expressions<br>
Expand Down Expand Up @@ -591,6 +591,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint-
[`@typescript-eslint/no-unnecessary-type-assertion`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md
[`@typescript-eslint/no-var-requires`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-var-requires.md
[`@typescript-eslint/type-annotation-spacing`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/type-annotation-spacing.md
[`@typescript-eslint/typedef`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/typedef.md
[`@typescript-eslint/unified-signatures`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/unified-signatures.md
[`@typescript-eslint/no-misused-new`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-misused-new.md
[`@typescript-eslint/no-object-literal-type-assertion`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-object-literal-type-assertion.md
Expand Down
264 changes: 264 additions & 0 deletions packages/eslint-plugin/docs/rules/typedef.md
@@ -0,0 +1,264 @@
# Require type annotations to exist (typedef)

TypeScript cannot always infer types for all places in code.
Some locations require type annotations for their types to be inferred.

```ts
class ContainsText {
// There must be a type annotation here to infer the type
delayedText: string;

// `typedef` requires a type annotation here to maintain consistency
immediateTextExplicit: string = 'text';

// This is still a string type because of its initial value
immediateTextImplicit = 'text';
}
```

> Note: requiring type annotations unnecessarily can be cumbersome to maintain and generally reduces code readability.
> TypeScript is often better at inferring types than easily written type annotations would allow.
> Instead of enabling `typedef`, it is generally recommended to use the `--noImplicitAny` and/or `--strictPropertyInitialization` compiler options to enforce type annotations only when useful.
## Rule Details

This rule can enforce type annotations in locations regardless of whether they're required.
This is typically used to maintain consistency for element types that sometimes require them.

> To enforce type definitions existing on call signatures as per TSLint's `arrow-call-signature` and `call-signature` options, use `explicit-function-return-type`.
## Options

This rule has an object option that may receive any of the following as booleans:

- `"arrayDestructuring"`
- `"arrowParameter"`: `true` by default
- `"memberVariableDeclaration"`: `true` by default
- `"objectDestructuring"`
- `"parameter"`: `true` by default
- `"propertyDeclaration"`: `true` by default
- `"variableDeclaration"`

For example, with the following configuration:

```json
{
"rules": {
"typedef": [
"error",
{
"arrowParameter": false,
"variableDeclaration": true
}
]
}
}
```

- Type annotations on arrow function parameters are not required
- Type annotations on variables are required
- Options otherwise adhere to the defaults

### arrayDestructuring

Whether to enforce type annotations on variables declared using array destructuring.

Examples of **incorrect** code with `{ "arrayDestructuring": true }`:

```ts
const [a] = [1];
const [b, c] = [1, 2];
```

Examples of **correct** code with `{ "arrayDestructuring": true }`:

```ts
const [a]: number[] = [1];
const [b]: [number] = [2];
const [c, d]: [boolean, string] = [true, 'text'];
```

### arrowParameter

Whether to enforce type annotations for parameters of arrow functions.
bradzacher marked this conversation as resolved.
Show resolved Hide resolved

Examples of **incorrect** code with `{ "arrowParameter": true }`:

```ts
const logsSize = size => console.log(size);

['hello', 'world'].map(text => text.length);

const mapper = {
map: text => text + '...',
};
```

Examples of **correct** code with `{ "arrowParameter": true }`:

```ts
const logsSize = (size: number) => console.log(text);

['hello', 'world'].map((text: string) => text.length);

const mapper = {
map: (text: string) => text + '...',
};
```

### memberVariableDeclaration

Whether to enforce type annotations on member variables of classes.
bradzacher marked this conversation as resolved.
Show resolved Hide resolved

Examples of **incorrect** code with `{ "memberVariableDeclaration": true }`:

```ts
class ContainsText {
delayedText;
immediateTextImplicit = 'text';
}
```

Examples of **correct** code with `{ "memberVariableDeclaration": true }`:

```ts
class ContainsText {
delayedText: string;
immediateTextImplicit: string = 'text';
}
```

### objectDestructuring

Whether to enforce type annotations on variables declared using object destructuring.

Examples of **incorrect** code with `{ "objectDestructuring": true }`:

```ts
const { length } = 'text';
const [b, c] = Math.random() ? [1, 2] : [3, 4];
```

Examples of **correct** code with `{ "objectDestructuring": true }`:

```ts
const { length }: { length: number } = 'text';
const [b, c]: [number, number] = Math.random() ? [1, 2] : [3, 4];
```

### parameter

Whether to enforce type annotations for parameters of functions and methods.
bradzacher marked this conversation as resolved.
Show resolved Hide resolved

Examples of **incorrect** code with `{ "parameter": true }`:

```ts
function logsSize(size): void {
console.log(size);
}

const doublesSize = function(size): numeber {
return size * 2;
};

const divider = {
curriesSize(size): number {
return size;
},
dividesSize: function(size): number {
return size / 2;
},
};

class Logger {
log(text): boolean {
console.log('>', text);
return true;
}
}
```

Examples of **correct** code with `{ "parameter": true }`:

```ts
function logsSize(size: number): void {
console.log(size);
}

const doublesSize = function(size: number): numeber {
return size * 2;
};

const divider = {
curriesSize(size: number): number {
return size;
},
dividesSize: function(size: number): number {
return size / 2;
},
};

class Logger {
log(text: boolean): boolean {
console.log('>', text);
return true;
}
}
```

### propertyDeclaration

Whether to enforce type annotations for properties of interfaces and types.

Examples of **incorrect** code with `{ "propertyDeclaration": true }`:

```ts
type Members = {
member;
otherMember;
};
```

Examples of **correct** code with `{ "propertyDeclaration": true }`:

```ts
type Members = {
member: boolean;
otherMember: string;
};
```

### variableDeclaration

Whether to enforce type annotations for variable declarations, excluding array and object destructuring.

Examples of **incorrect** code with `{ "variableDeclaration": true }`:

```ts
const text = 'text';
let initialText = 'text';
let delayedText;
bradzacher marked this conversation as resolved.
Show resolved Hide resolved
```

Examples of **correct** code with `{ "variableDeclaration": true }`:

```ts
const text: string = 'text';
let initialText: string = 'text';
let delayedText: string;
```

## When Not To Use It

If you are using stricter TypeScript compiler options, particularly `--noImplicitAny` and/or `--strictPropertyInitialization`, you likely don't need this rule.

In general, if you do not consider the cost of writing unnecessary type annotations reasonable, then do not use this rule.

## Further Reading

- [TypeScript Type System](https://basarat.gitbooks.io/typescript/docs/types/type-system.html)
- [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html)

## Compatibility

- TSLint: [typedef](https://palantir.github.io/tslint/rules/typedef)
1 change: 1 addition & 0 deletions packages/eslint-plugin/src/configs/all.json
Expand Up @@ -68,6 +68,7 @@
"@typescript-eslint/strict-boolean-expressions": "error",
"@typescript-eslint/triple-slash-reference": "error",
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/typedef": "error",
"@typescript-eslint/unbound-method": "error",
"@typescript-eslint/unified-signatures": "error"
}
Expand Down
4 changes: 3 additions & 1 deletion packages/eslint-plugin/src/rules/index.ts
Expand Up @@ -57,6 +57,7 @@ import semi from './semi';
import strictBooleanExpressions from './strict-boolean-expressions';
import tripleSlashReference from './triple-slash-reference';
import typeAnnotationSpacing from './type-annotation-spacing';
import typedef from './typedef';
import unboundMethod from './unbound-method';
import unifiedSignatures from './unified-signatures';

Expand Down Expand Up @@ -116,10 +117,11 @@ export default {
'promise-function-async': promiseFunctionAsync,
'require-array-sort-compare': requireArraySortCompare,
'restrict-plus-operands': restrictPlusOperands,
semi: semi,
'strict-boolean-expressions': strictBooleanExpressions,
'triple-slash-reference': tripleSlashReference,
'type-annotation-spacing': typeAnnotationSpacing,
'unbound-method': unboundMethod,
'unified-signatures': unifiedSignatures,
semi: semi,
typedef: typedef,
};