Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 753ad75

Browse files
Dilatorilybradzacher
authored andcommitted
feat(eslint-plugin): [no-explicit-any] ignoreRestArgs (typescript-eslint#548)
1 parent c4df4ff commit 753ad75

File tree

3 files changed

+291
-3
lines changed

3 files changed

+291
-3
lines changed

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,52 @@ function greet(param: Array<string>): string {}
8787
function greet(param: Array<string>): Array<string> {}
8888
```
8989

90+
### ignoreRestArgs
91+
92+
A boolean to specify if arrays from the rest operator are considered okay. `false` by default.
93+
94+
Examples of **incorrect** code for the `{ "ignoreRestArgs": false }` option:
95+
96+
```ts
97+
/*eslint @typescript-eslint/no-explicit-any: ["error", { "ignoreRestArgs": false }]*/
98+
99+
function foo1(...args: any[]): void {}
100+
function foo2(...args: readonly any[]): void {}
101+
function foo3(...args: Array<any>): void {}
102+
function foo4(...args: ReadonlyArray<any>): void {}
103+
104+
const bar1 = (...args: any[]): void {}
105+
const bar2 = (...args: readonly any[]): void {}
106+
const bar3 = (...args: Array<any>): void {}
107+
const bar4 = (...args: ReadonlyArray<any>): void {}
108+
109+
const baz1 = function (...args: any[]) {}
110+
const baz2 = function (...args: readonly any[]) {}
111+
const baz3 = function (...args: Array<any>) {}
112+
const baz4 = function (...args: ReadonlyArray<any>) {}
113+
```
114+
115+
Examples of **correct** code for the `{ "ignoreRestArgs": true }` option:
116+
117+
```ts
118+
/*eslint @typescript-eslint/no-explicit-any: ["error", { "ignoreRestArgs": true }]*/
119+
120+
function foo1(...args: any[]): void {}
121+
function foo2(...args: readonly any[]): void {}
122+
function foo3(...args: Array<any>): void {}
123+
function foo4(...args: ReadonlyArray<any>): void {}
124+
125+
const bar1 = (...args: any[]): void {}
126+
const bar2 = (...args: readonly any[]): void {}
127+
const bar3 = (...args: Array<any>): void {}
128+
const bar4 = (...args: ReadonlyArray<any>): void {}
129+
130+
const baz1 = function (...args: any[]) {}
131+
const baz2 = function (...args: readonly any[]) {}
132+
const baz3 = function (...args: Array<any>) {}
133+
const baz4 = function (...args: ReadonlyArray<any>) {}
134+
```
135+
90136
## When Not To Use It
91137

92138
If an unknown type or a library without typings is used

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

Lines changed: 140 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import {
2+
TSESTree,
3+
AST_NODE_TYPES,
4+
} from '@typescript-eslint/experimental-utils';
15
import * as util from '../util';
26

37
export default util.createRule({
@@ -12,12 +16,145 @@ export default util.createRule({
1216
messages: {
1317
unexpectedAny: 'Unexpected any. Specify a different type.',
1418
},
15-
schema: [],
19+
schema: [
20+
{
21+
type: 'object',
22+
additionalProperties: false,
23+
properties: {
24+
ignoreRestArgs: {
25+
type: 'boolean',
26+
},
27+
},
28+
},
29+
],
1630
},
17-
defaultOptions: [],
18-
create(context) {
31+
defaultOptions: [
32+
{
33+
ignoreRestArgs: false,
34+
},
35+
],
36+
create(context, [{ ignoreRestArgs }]) {
37+
/**
38+
* Checks if the node is an arrow function, function declaration or function expression
39+
* @param node the node to be validated.
40+
* @returns true if the node is an arrow function, function declaration or function expression
41+
* @private
42+
*/
43+
function isNodeValidFunction(node: TSESTree.Node): boolean {
44+
return [
45+
AST_NODE_TYPES.ArrowFunctionExpression,
46+
AST_NODE_TYPES.FunctionDeclaration,
47+
AST_NODE_TYPES.FunctionExpression,
48+
].includes(node.type);
49+
}
50+
51+
/**
52+
* Checks if the node is a rest element child node of a function
53+
* @param node the node to be validated.
54+
* @returns true if the node is a rest element child node of a function
55+
* @private
56+
*/
57+
function isNodeRestElementInFunction(node: TSESTree.Node): boolean {
58+
return (
59+
node.type === AST_NODE_TYPES.RestElement &&
60+
typeof node.parent !== 'undefined' &&
61+
isNodeValidFunction(node.parent)
62+
);
63+
}
64+
65+
/**
66+
* Checks if the node is a TSTypeOperator node with a readonly operator
67+
* @param node the node to be validated.
68+
* @returns true if the node is a TSTypeOperator node with a readonly operator
69+
* @private
70+
*/
71+
function isNodeReadonlyTSTypeOperator(node: TSESTree.Node): boolean {
72+
return (
73+
node.type === AST_NODE_TYPES.TSTypeOperator &&
74+
node.operator === 'readonly'
75+
);
76+
}
77+
78+
/**
79+
* Checks if the node is a TSTypeReference node with an Array identifier
80+
* @param node the node to be validated.
81+
* @returns true if the node is a TSTypeReference node with an Array identifier
82+
* @private
83+
*/
84+
function isNodeValidArrayTSTypeReference(node: TSESTree.Node): boolean {
85+
return (
86+
node.type === AST_NODE_TYPES.TSTypeReference &&
87+
typeof node.typeName !== 'undefined' &&
88+
node.typeName.type === AST_NODE_TYPES.Identifier &&
89+
['Array', 'ReadonlyArray'].includes(node.typeName.name)
90+
);
91+
}
92+
93+
/**
94+
* Checks if the node is a valid TSTypeOperator or TSTypeReference node
95+
* @param node the node to be validated.
96+
* @returns true if the node is a valid TSTypeOperator or TSTypeReference node
97+
* @private
98+
*/
99+
function isNodeValidTSType(node: TSESTree.Node): boolean {
100+
return (
101+
isNodeReadonlyTSTypeOperator(node) ||
102+
isNodeValidArrayTSTypeReference(node)
103+
);
104+
}
105+
106+
/**
107+
* Checks if the great grand-parent node is a RestElement node in a function
108+
* @param node the node to be validated.
109+
* @returns true if the great grand-parent node is a RestElement node in a function
110+
* @private
111+
*/
112+
function isGreatGrandparentRestElement(node: TSESTree.Node): boolean {
113+
return (
114+
typeof node.parent !== 'undefined' &&
115+
typeof node.parent.parent !== 'undefined' &&
116+
typeof node.parent.parent.parent !== 'undefined' &&
117+
isNodeRestElementInFunction(node.parent.parent.parent)
118+
);
119+
}
120+
121+
/**
122+
* Checks if the great great grand-parent node is a valid RestElement node in a function
123+
* @param node the node to be validated.
124+
* @returns true if the great great grand-parent node is a valid RestElement node in a function
125+
* @private
126+
*/
127+
function isGreatGreatGrandparentRestElement(node: TSESTree.Node): boolean {
128+
return (
129+
typeof node.parent !== 'undefined' &&
130+
typeof node.parent.parent !== 'undefined' &&
131+
isNodeValidTSType(node.parent.parent) &&
132+
typeof node.parent.parent.parent !== 'undefined' &&
133+
typeof node.parent.parent.parent.parent !== 'undefined' &&
134+
isNodeRestElementInFunction(node.parent.parent.parent.parent)
135+
);
136+
}
137+
138+
/**
139+
* Checks if the great grand-parent or the great great grand-parent node is a RestElement node
140+
* @param node the node to be validated.
141+
* @returns true if the great grand-parent or the great great grand-parent node is a RestElement node
142+
* @private
143+
*/
144+
function isNodeDescendantOfRestElementInFunction(
145+
node: TSESTree.Node,
146+
): boolean {
147+
return (
148+
isGreatGrandparentRestElement(node) ||
149+
isGreatGreatGrandparentRestElement(node)
150+
);
151+
}
152+
19153
return {
20154
TSAnyKeyword(node) {
155+
if (ignoreRestArgs && isNodeDescendantOfRestElementInFunction(node)) {
156+
return;
157+
}
21158
context.report({
22159
node,
23160
messageId: 'unexpectedAny',

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

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,63 @@ type obj = {
129129
message: string & Array<Array<string>>;
130130
}
131131
`,
132+
// https://github.com/eslint/typescript-eslint-parser/issues/397
133+
{
134+
code: `
135+
function foo(a: number, ...rest: any[]): void {
136+
return;
137+
}
138+
`,
139+
options: [{ ignoreRestArgs: true }],
140+
},
141+
{
142+
code: `function foo1(...args: any[]) {}`,
143+
options: [{ ignoreRestArgs: true }],
144+
},
145+
{
146+
code: `const bar1 = function (...args: any[]) {}`,
147+
options: [{ ignoreRestArgs: true }],
148+
},
149+
{
150+
code: `const baz1 = (...args: any[]) => {}`,
151+
options: [{ ignoreRestArgs: true }],
152+
},
153+
{
154+
code: `function foo2(...args: readonly any[]) {}`,
155+
options: [{ ignoreRestArgs: true }],
156+
},
157+
{
158+
code: `const bar2 = function (...args: readonly any[]) {}`,
159+
options: [{ ignoreRestArgs: true }],
160+
},
161+
{
162+
code: `const baz2 = (...args: readonly any[]) => {}`,
163+
options: [{ ignoreRestArgs: true }],
164+
},
165+
{
166+
code: `function foo3(...args: Array<any>) {}`,
167+
options: [{ ignoreRestArgs: true }],
168+
},
169+
{
170+
code: `const bar3 = function (...args: Array<any>) {}`,
171+
options: [{ ignoreRestArgs: true }],
172+
},
173+
{
174+
code: `const baz3 = (...args: Array<any>) => {}`,
175+
options: [{ ignoreRestArgs: true }],
176+
},
177+
{
178+
code: `function foo4(...args: ReadonlyArray<any>) {}`,
179+
options: [{ ignoreRestArgs: true }],
180+
},
181+
{
182+
code: `const bar4 = function (...args: ReadonlyArray<any>) {}`,
183+
options: [{ ignoreRestArgs: true }],
184+
},
185+
{
186+
code: `const baz4 = (...args: ReadonlyArray<any>) => {}`,
187+
options: [{ ignoreRestArgs: true }],
188+
},
132189
],
133190
invalid: [
134191
{
@@ -679,5 +736,53 @@ type obj = {
679736
},
680737
],
681738
},
739+
{
740+
// https://github.com/eslint/typescript-eslint-parser/issues/397
741+
code: `
742+
function foo(a: number, ...rest: any[]): void {
743+
return;
744+
}
745+
`,
746+
errors: [
747+
{
748+
messageId: 'unexpectedAny',
749+
line: 2,
750+
column: 42,
751+
},
752+
],
753+
},
754+
{
755+
code: `function foo5(...args: any) {}`,
756+
options: [{ ignoreRestArgs: true }],
757+
errors: [
758+
{
759+
messageId: 'unexpectedAny',
760+
line: 1,
761+
column: 24,
762+
},
763+
],
764+
},
765+
{
766+
code: `const bar5 = function (...args: any) {}`,
767+
options: [{ ignoreRestArgs: true }],
768+
errors: [
769+
{
770+
messageId: 'unexpectedAny',
771+
line: 1,
772+
column: 33,
773+
},
774+
],
775+
},
776+
{
777+
code: `const baz5 = (...args: any) => {}`,
778+
options: [{ ignoreRestArgs: true }],
779+
errors: [
780+
{
781+
messageId: 'unexpectedAny',
782+
line: 1,
783+
column: 24,
784+
},
785+
],
786+
},
682787
],
683788
});

0 commit comments

Comments
 (0)