Skip to content

Commit 35cc99b

Browse files
Josh GoldbergJamesHenry
Josh Goldberg
authored andcommitted
feat(eslint-plugin): added new rule typedef (#581)
1 parent 4893aec commit 35cc99b

File tree

7 files changed

+883
-2
lines changed

7 files changed

+883
-2
lines changed

Diff for: packages/eslint-plugin/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e
181181
| [`@typescript-eslint/strict-boolean-expressions`](./docs/rules/strict-boolean-expressions.md) | Restricts the types allowed in boolean expressions | | | :thought_balloon: |
182182
| [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Sets preference level for triple slash directives versus ES6-style import declarations | | | |
183183
| [`@typescript-eslint/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) | Require consistent spacing around type annotations | :heavy_check_mark: | :wrench: | |
184+
| [`@typescript-eslint/typedef`](./docs/rules/typedef.md) | Requires type annotations to exist | | | |
184185
| [`@typescript-eslint/unbound-method`](./docs/rules/unbound-method.md) | Enforces unbound methods are called with their expected scope | | | :thought_balloon: |
185186
| [`@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 | | | |
186187

Diff for: packages/eslint-plugin/ROADMAP.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
| [`only-arrow-functions`] | 🔌 | [`prefer-arrow/prefer-arrow-functions`] |
3333
| [`prefer-for-of`] || [`@typescript-eslint/prefer-for-of`] |
3434
| [`promise-function-async`] || [`@typescript-eslint/promise-function-async`] |
35-
| [`typedef`] | 🛑 | N/A |
3635
| [`typedef-whitespace`] || [`@typescript-eslint/type-annotation-spacing`] |
36+
| [`typedef`] || [`@typescript-eslint/typedef`] |
3737
| [`unified-signatures`] || [`@typescript-eslint/unified-signatures`] |
3838

3939
<sup>[1]</sup> The ESLint rule only supports exact string matching, rather than regular expressions<br>
@@ -592,6 +592,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint-
592592
[`@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
593593
[`@typescript-eslint/no-var-requires`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-var-requires.md
594594
[`@typescript-eslint/type-annotation-spacing`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/type-annotation-spacing.md
595+
[`@typescript-eslint/typedef`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/typedef.md
595596
[`@typescript-eslint/unified-signatures`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/unified-signatures.md
596597
[`@typescript-eslint/no-misused-new`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-misused-new.md
597598
[`@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

Diff for: packages/eslint-plugin/docs/rules/typedef.md

+264
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
# Require type annotations to exist (typedef)
2+
3+
TypeScript cannot always infer types for all places in code.
4+
Some locations require type annotations for their types to be inferred.
5+
6+
```ts
7+
class ContainsText {
8+
// There must be a type annotation here to infer the type
9+
delayedText: string;
10+
11+
// `typedef` requires a type annotation here to maintain consistency
12+
immediateTextExplicit: string = 'text';
13+
14+
// This is still a string type because of its initial value
15+
immediateTextImplicit = 'text';
16+
}
17+
```
18+
19+
> Note: requiring type annotations unnecessarily can be cumbersome to maintain and generally reduces code readability.
20+
> TypeScript is often better at inferring types than easily written type annotations would allow.
21+
> Instead of enabling `typedef`, it is generally recommended to use the `--noImplicitAny` and/or `--strictPropertyInitialization` compiler options to enforce type annotations only when useful.
22+
23+
## Rule Details
24+
25+
This rule can enforce type annotations in locations regardless of whether they're required.
26+
This is typically used to maintain consistency for element types that sometimes require them.
27+
28+
> To enforce type definitions existing on call signatures as per TSLint's `arrow-call-signature` and `call-signature` options, use `explicit-function-return-type`.
29+
30+
## Options
31+
32+
This rule has an object option that may receive any of the following as booleans:
33+
34+
- `"arrayDestructuring"`
35+
- `"arrowParameter"`: `true` by default
36+
- `"memberVariableDeclaration"`: `true` by default
37+
- `"objectDestructuring"`
38+
- `"parameter"`: `true` by default
39+
- `"propertyDeclaration"`: `true` by default
40+
- `"variableDeclaration"`
41+
42+
For example, with the following configuration:
43+
44+
```json
45+
{
46+
"rules": {
47+
"typedef": [
48+
"error",
49+
{
50+
"arrowParameter": false,
51+
"variableDeclaration": true
52+
}
53+
]
54+
}
55+
}
56+
```
57+
58+
- Type annotations on arrow function parameters are not required
59+
- Type annotations on variables are required
60+
- Options otherwise adhere to the defaults
61+
62+
### arrayDestructuring
63+
64+
Whether to enforce type annotations on variables declared using array destructuring.
65+
66+
Examples of **incorrect** code with `{ "arrayDestructuring": true }`:
67+
68+
```ts
69+
const [a] = [1];
70+
const [b, c] = [1, 2];
71+
```
72+
73+
Examples of **correct** code with `{ "arrayDestructuring": true }`:
74+
75+
```ts
76+
const [a]: number[] = [1];
77+
const [b]: [number] = [2];
78+
const [c, d]: [boolean, string] = [true, 'text'];
79+
```
80+
81+
### arrowParameter
82+
83+
Whether to enforce type annotations for parameters of arrow functions.
84+
85+
Examples of **incorrect** code with `{ "arrowParameter": true }`:
86+
87+
```ts
88+
const logsSize = size => console.log(size);
89+
90+
['hello', 'world'].map(text => text.length);
91+
92+
const mapper = {
93+
map: text => text + '...',
94+
};
95+
```
96+
97+
Examples of **correct** code with `{ "arrowParameter": true }`:
98+
99+
```ts
100+
const logsSize = (size: number) => console.log(text);
101+
102+
['hello', 'world'].map((text: string) => text.length);
103+
104+
const mapper = {
105+
map: (text: string) => text + '...',
106+
};
107+
```
108+
109+
### memberVariableDeclaration
110+
111+
Whether to enforce type annotations on member variables of classes.
112+
113+
Examples of **incorrect** code with `{ "memberVariableDeclaration": true }`:
114+
115+
```ts
116+
class ContainsText {
117+
delayedText;
118+
immediateTextImplicit = 'text';
119+
}
120+
```
121+
122+
Examples of **correct** code with `{ "memberVariableDeclaration": true }`:
123+
124+
```ts
125+
class ContainsText {
126+
delayedText: string;
127+
immediateTextImplicit: string = 'text';
128+
}
129+
```
130+
131+
### objectDestructuring
132+
133+
Whether to enforce type annotations on variables declared using object destructuring.
134+
135+
Examples of **incorrect** code with `{ "objectDestructuring": true }`:
136+
137+
```ts
138+
const { length } = 'text';
139+
const [b, c] = Math.random() ? [1, 2] : [3, 4];
140+
```
141+
142+
Examples of **correct** code with `{ "objectDestructuring": true }`:
143+
144+
```ts
145+
const { length }: { length: number } = 'text';
146+
const [b, c]: [number, number] = Math.random() ? [1, 2] : [3, 4];
147+
```
148+
149+
### parameter
150+
151+
Whether to enforce type annotations for parameters of functions and methods.
152+
153+
Examples of **incorrect** code with `{ "parameter": true }`:
154+
155+
```ts
156+
function logsSize(size): void {
157+
console.log(size);
158+
}
159+
160+
const doublesSize = function(size): numeber {
161+
return size * 2;
162+
};
163+
164+
const divider = {
165+
curriesSize(size): number {
166+
return size;
167+
},
168+
dividesSize: function(size): number {
169+
return size / 2;
170+
},
171+
};
172+
173+
class Logger {
174+
log(text): boolean {
175+
console.log('>', text);
176+
return true;
177+
}
178+
}
179+
```
180+
181+
Examples of **correct** code with `{ "parameter": true }`:
182+
183+
```ts
184+
function logsSize(size: number): void {
185+
console.log(size);
186+
}
187+
188+
const doublesSize = function(size: number): numeber {
189+
return size * 2;
190+
};
191+
192+
const divider = {
193+
curriesSize(size: number): number {
194+
return size;
195+
},
196+
dividesSize: function(size: number): number {
197+
return size / 2;
198+
},
199+
};
200+
201+
class Logger {
202+
log(text: boolean): boolean {
203+
console.log('>', text);
204+
return true;
205+
}
206+
}
207+
```
208+
209+
### propertyDeclaration
210+
211+
Whether to enforce type annotations for properties of interfaces and types.
212+
213+
Examples of **incorrect** code with `{ "propertyDeclaration": true }`:
214+
215+
```ts
216+
type Members = {
217+
member;
218+
otherMember;
219+
};
220+
```
221+
222+
Examples of **correct** code with `{ "propertyDeclaration": true }`:
223+
224+
```ts
225+
type Members = {
226+
member: boolean;
227+
otherMember: string;
228+
};
229+
```
230+
231+
### variableDeclaration
232+
233+
Whether to enforce type annotations for variable declarations, excluding array and object destructuring.
234+
235+
Examples of **incorrect** code with `{ "variableDeclaration": true }`:
236+
237+
```ts
238+
const text = 'text';
239+
let initialText = 'text';
240+
let delayedText;
241+
```
242+
243+
Examples of **correct** code with `{ "variableDeclaration": true }`:
244+
245+
```ts
246+
const text: string = 'text';
247+
let initialText: string = 'text';
248+
let delayedText: string;
249+
```
250+
251+
## When Not To Use It
252+
253+
If you are using stricter TypeScript compiler options, particularly `--noImplicitAny` and/or `--strictPropertyInitialization`, you likely don't need this rule.
254+
255+
In general, if you do not consider the cost of writing unnecessary type annotations reasonable, then do not use this rule.
256+
257+
## Further Reading
258+
259+
- [TypeScript Type System](https://basarat.gitbooks.io/typescript/docs/types/type-system.html)
260+
- [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html)
261+
262+
## Compatibility
263+
264+
- TSLint: [typedef](https://palantir.github.io/tslint/rules/typedef)

Diff for: packages/eslint-plugin/src/configs/all.json

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"@typescript-eslint/strict-boolean-expressions": "error",
7070
"@typescript-eslint/triple-slash-reference": "error",
7171
"@typescript-eslint/type-annotation-spacing": "error",
72+
"@typescript-eslint/typedef": "error",
7273
"@typescript-eslint/unbound-method": "error",
7374
"@typescript-eslint/unified-signatures": "error"
7475
}

Diff for: packages/eslint-plugin/src/rules/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import semi from './semi';
5656
import strictBooleanExpressions from './strict-boolean-expressions';
5757
import tripleSlashReference from './triple-slash-reference';
5858
import typeAnnotationSpacing from './type-annotation-spacing';
59+
import typedef from './typedef';
5960
import unboundMethod from './unbound-method';
6061
import unifiedSignatures from './unified-signatures';
6162

@@ -114,10 +115,11 @@ export default {
114115
'require-array-sort-compare': requireArraySortCompare,
115116
'require-await': requireAwait,
116117
'restrict-plus-operands': restrictPlusOperands,
117-
semi: semi,
118118
'strict-boolean-expressions': strictBooleanExpressions,
119119
'triple-slash-reference': tripleSlashReference,
120120
'type-annotation-spacing': typeAnnotationSpacing,
121121
'unbound-method': unboundMethod,
122122
'unified-signatures': unifiedSignatures,
123+
semi: semi,
124+
typedef: typedef,
123125
};

0 commit comments

Comments
 (0)