Skip to content

Commit 606fc70

Browse files
aggmoulikbradzacher
authored andcommitted
feat(eslint-plugin): [no-explicit-any] Add an optional fixer (#609)
1 parent 76b89a5 commit 606fc70

File tree

4 files changed

+67
-6
lines changed

4 files changed

+67
-6
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e
145145
| [`@typescript-eslint/no-array-constructor`](./docs/rules/no-array-constructor.md) | Disallow generic `Array` constructors | :heavy_check_mark: | :wrench: | |
146146
| [`@typescript-eslint/no-empty-function`](./docs/rules/no-empty-function.md) | Disallow empty functions | | | |
147147
| [`@typescript-eslint/no-empty-interface`](./docs/rules/no-empty-interface.md) | Disallow the declaration of empty interfaces | :heavy_check_mark: | | |
148-
| [`@typescript-eslint/no-explicit-any`](./docs/rules/no-explicit-any.md) | Disallow usage of the `any` type | :heavy_check_mark: | | |
148+
| [`@typescript-eslint/no-explicit-any`](./docs/rules/no-explicit-any.md) | Disallow usage of the `any` type | :heavy_check_mark: | :wrench: | |
149149
| [`@typescript-eslint/no-extra-parens`](./docs/rules/no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | |
150150
| [`@typescript-eslint/no-extraneous-class`](./docs/rules/no-extraneous-class.md) | Forbids the use of classes as namespaces | | | |
151151
| [`@typescript-eslint/no-floating-promises`](./docs/rules/no-floating-promises.md) | Requires Promise-like values to be handled appropriately. | | | :thought_balloon: |

Diff for: packages/eslint-plugin/docs/rules/no-explicit-any.md

+18
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,24 @@ function greet(param: Array<string>): string {}
8787
function greet(param: Array<string>): Array<string> {}
8888
```
8989

90+
## Options
91+
92+
The rule accepts an options object with the following properties:
93+
94+
```ts
95+
type Options = {
96+
// if true, auto-fixing will be made available in which the "any" type is converted to an "unknown" type
97+
fixToUnknown: boolean;
98+
// specify if arrays from the rest operator are considered okay
99+
ignoreRestArgs: boolean;
100+
};
101+
102+
const defaults = {
103+
fixToUnknown: false,
104+
ignoreRestArgs: false,
105+
};
106+
```
107+
90108
### ignoreRestArgs
91109

92110
A boolean to specify if arrays from the rest operator are considered okay. `false` by default.

Diff for: packages/eslint-plugin/src/rules/no-explicit-any.ts

+24-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@ import {
33
AST_NODE_TYPES,
44
} from '@typescript-eslint/experimental-utils';
55
import * as util from '../util';
6+
import { TSESLint } from '@typescript-eslint/experimental-utils';
67

7-
export default util.createRule({
8+
export type Options = [
9+
{
10+
fixToUnknown?: boolean;
11+
ignoreRestArgs?: boolean;
12+
}
13+
];
14+
export type MessageIds = 'unexpectedAny';
15+
16+
export default util.createRule<Options, MessageIds>({
817
name: 'no-explicit-any',
918
meta: {
1019
type: 'suggestion',
@@ -13,6 +22,7 @@ export default util.createRule({
1322
category: 'Best Practices',
1423
recommended: 'warn',
1524
},
25+
fixable: 'code',
1626
messages: {
1727
unexpectedAny: 'Unexpected any. Specify a different type.',
1828
},
@@ -21,6 +31,9 @@ export default util.createRule({
2131
type: 'object',
2232
additionalProperties: false,
2333
properties: {
34+
fixToUnknown: {
35+
type: 'boolean',
36+
},
2437
ignoreRestArgs: {
2538
type: 'boolean',
2639
},
@@ -30,10 +43,11 @@ export default util.createRule({
3043
},
3144
defaultOptions: [
3245
{
46+
fixToUnknown: false,
3347
ignoreRestArgs: false,
3448
},
3549
],
36-
create(context, [{ ignoreRestArgs }]) {
50+
create(context, [{ ignoreRestArgs, fixToUnknown }]) {
3751
/**
3852
* Checks if the node is an arrow function, function declaration or function expression
3953
* @param node the node to be validated.
@@ -155,9 +169,17 @@ export default util.createRule({
155169
if (ignoreRestArgs && isNodeDescendantOfRestElementInFunction(node)) {
156170
return;
157171
}
172+
173+
let fix: TSESLint.ReportFixFunction | null = null;
174+
175+
if (fixToUnknown) {
176+
fix = fixer => fixer.replaceText(node, 'unknown');
177+
}
178+
158179
context.report({
159180
node,
160181
messageId: 'unexpectedAny',
182+
fix,
161183
});
162184
},
163185
};

Diff for: packages/eslint-plugin/tests/rules/no-explicit-any.test.ts

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import rule from '../../src/rules/no-explicit-any';
1+
import rule, { MessageIds, Options } from '../../src/rules/no-explicit-any';
22
import { RuleTester } from '../RuleTester';
3+
import { TSESLint } from '@typescript-eslint/experimental-utils';
4+
5+
type InvalidTestCase = TSESLint.InvalidTestCase<MessageIds, Options>;
36

47
const ruleTester = new RuleTester({
58
parser: '@typescript-eslint/parser',
@@ -187,7 +190,7 @@ type obj = {
187190
options: [{ ignoreRestArgs: true }],
188191
},
189192
],
190-
invalid: [
193+
invalid: ([
191194
{
192195
code: 'const number: any = 1',
193196
errors: [
@@ -784,5 +787,23 @@ type obj = {
784787
},
785788
],
786789
},
787-
],
790+
] as InvalidTestCase[]).reduce<InvalidTestCase[]>((acc, testCase) => {
791+
acc.push(testCase);
792+
const options = testCase.options || [];
793+
const code = `// fixToUnknown: true\n${testCase.code}`;
794+
acc.push({
795+
code,
796+
output: code.replace(/any/g, 'unknown'),
797+
options: [{ ...options[0], fixToUnknown: true }],
798+
errors: testCase.errors.map(err => {
799+
if (err.line === undefined) {
800+
return err;
801+
}
802+
803+
return { ...err, line: err.line + 1 };
804+
}),
805+
});
806+
807+
return acc;
808+
}, []),
788809
});

0 commit comments

Comments
 (0)