diff --git a/packages/eslint-plugin/docs/rules/no-explicit-any.md b/packages/eslint-plugin/docs/rules/no-explicit-any.md index 0c271392349..c440d3bdc60 100644 --- a/packages/eslint-plugin/docs/rules/no-explicit-any.md +++ b/packages/eslint-plugin/docs/rules/no-explicit-any.md @@ -119,30 +119,23 @@ function foo2(...args: readonly any[]): void {} function foo3(...args: Array): void {} function foo4(...args: ReadonlyArray): void {} -const bar1 = (...args: any[]): void {} -const bar2 = (...args: readonly any[]): void {} -const bar3 = (...args: Array): void {} -const bar4 = (...args: ReadonlyArray): void {} +declare function bar(...args: any[]): void; -const baz1 = function (...args: any[]) {} -const baz2 = function (...args: readonly any[]) {} -const baz3 = function (...args: Array) {} -const baz4 = function (...args: ReadonlyArray) {} +const baz = (...args: any[]) => {}; +const qux = function(...args: any[]) {}; -interface Qux1 { (...args: any[]): void; } -interface Qux2 { (...args: readonly any[]): void; } -interface Qux3 { (...args: Array): void; } -interface Qux4 { (...args: ReadonlyArray): void; } +type Quux = (...args: any[]) => void; +type Quuz = new (...args: any[]) => void; -function quux1(fn: (...args: any[]) => void): void {} -function quux2(fn: (...args: readonly any[]) => void): void {} -function quux3(fn: (...args: Array) => void): void {} -function quux4(fn: (...args: ReadonlyArray) => void): void {} - -function quuz1(): ((...args: any[]) => void) {} -function quuz2(): ((...args: readonly any[]) => void) {} -function quuz3(): ((...args: Array) => void) {} -function quuz4(): ((...args: ReadonlyArray) => void) {} +interface Grault { + (...args: any[]): void; +} +interface Corge { + new (...args: any[]): void; +} +interface Garply { + f(...args: any[]): void; +} ``` Examples of **correct** code for the `{ "ignoreRestArgs": true }` option: @@ -155,30 +148,23 @@ function foo2(...args: readonly any[]): void {} function foo3(...args: Array): void {} function foo4(...args: ReadonlyArray): void {} -const bar1 = (...args: any[]): void {} -const bar2 = (...args: readonly any[]): void {} -const bar3 = (...args: Array): void {} -const bar4 = (...args: ReadonlyArray): void {} - -const baz1 = function (...args: any[]) {} -const baz2 = function (...args: readonly any[]) {} -const baz3 = function (...args: Array) {} -const baz4 = function (...args: ReadonlyArray) {} - -interface Qux1 { (...args: any[]): void; } -interface Qux2 { (...args: readonly any[]): void; } -interface Qux3 { (...args: Array): void; } -interface Qux4 { (...args: ReadonlyArray): void; } - -function quux1(fn: (...args: any[]) => void): void {} -function quux2(fn: (...args: readonly any[]) => void): void {} -function quux3(fn: (...args: Array) => void): void {} -function quux4(fn: (...args: ReadonlyArray) => void): void {} - -function quuz1(): ((...args: any[]) => void) {} -function quuz2(): ((...args: readonly any[]) => void) {} -function quuz3(): ((...args: Array) => void) {} -function quuz4(): ((...args: ReadonlyArray) => void) {} +declare function bar(...args: any[]): void; + +const baz = (...args: any[]) => {}; +const qux = function(...args: any[]) {}; + +type Quux = (...args: any[]) => void; +type Quuz = new (...args: any[]) => void; + +interface Grault { + (...args: any[]): void; +} +interface Corge { + new (...args: any[]): void; +} +interface Garply { + f(...args: any[]): void; +} ``` ## When Not To Use It diff --git a/packages/eslint-plugin/src/rules/no-explicit-any.ts b/packages/eslint-plugin/src/rules/no-explicit-any.ts index 91765c40e26..795dae01725 100644 --- a/packages/eslint-plugin/src/rules/no-explicit-any.ts +++ b/packages/eslint-plugin/src/rules/no-explicit-any.ts @@ -53,18 +53,23 @@ export default util.createRule({ ], create(context, [{ ignoreRestArgs, fixToUnknown }]) { /** - * Checks if the node is an arrow function, function declaration or function expression + * Checks if the node is an arrow function, function/constructor declaration or function expression * @param node the node to be validated. - * @returns true if the node is an arrow function, function declaration, function expression, function type, or call signature + * @returns true if the node is any kind of function declaration or expression * @private */ function isNodeValidFunction(node: TSESTree.Node): boolean { return [ - AST_NODE_TYPES.ArrowFunctionExpression, - AST_NODE_TYPES.FunctionDeclaration, - AST_NODE_TYPES.FunctionExpression, - AST_NODE_TYPES.TSFunctionType, - AST_NODE_TYPES.TSCallSignatureDeclaration, + AST_NODE_TYPES.ArrowFunctionExpression, // const x = (...args: any[]) => {}; + AST_NODE_TYPES.FunctionDeclaration, // function f(...args: any[]) {} + AST_NODE_TYPES.FunctionExpression, // const x = function(...args: any[]) {}; + AST_NODE_TYPES.TSEmptyBodyFunctionExpression, // declare class A { f(...args: any[]): unknown; } + AST_NODE_TYPES.TSFunctionType, // type T = (...args: any[]) => unknown; + AST_NODE_TYPES.TSConstructorType, // type T = new (...args: any[]) => unknown + AST_NODE_TYPES.TSCallSignatureDeclaration, // type T = {(...args: any[]): unknown}; + AST_NODE_TYPES.TSConstructSignatureDeclaration, // type T = {new (...args: any[]): unknown}; + AST_NODE_TYPES.TSMethodSignature, // type T = {f(...args: any[]): unknown}; + AST_NODE_TYPES.TSDeclareFunction, // declare function _8(...args: any[]): unknown; ].includes(node.type); } diff --git a/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts b/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts index dde8b938175..1a5b9c5778c 100644 --- a/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts +++ b/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts @@ -255,6 +255,118 @@ interface Qux4 { code: 'function quuz4(): (...args: ReadonlyArray) => void {}', options: [{ ignoreRestArgs: true }], }, + { + code: 'type Fred1 = (...args: any[]) => void;', + options: [{ ignoreRestArgs: true }], + }, + { + code: 'type Fred2 = (...args: readonly any[]) => void;', + options: [{ ignoreRestArgs: true }], + }, + { + code: 'type Fred3 = (...args: Array) => void;', + options: [{ ignoreRestArgs: true }], + }, + { + code: 'type Fred4 = (...args: ReadonlyArray) => void;', + options: [{ ignoreRestArgs: true }], + }, + { + code: 'type Corge1 = new (...args: any[]) => void;', + options: [{ ignoreRestArgs: true }], + }, + { + code: 'type Corge2 = new (...args: readonly any[]) => void;', + options: [{ ignoreRestArgs: true }], + }, + { + code: 'type Corge3 = new (...args: Array) => void;', + options: [{ ignoreRestArgs: true }], + }, + { + code: 'type Corge4 = new (...args: ReadonlyArray) => void;', + options: [{ ignoreRestArgs: true }], + }, + { + code: ` +interface Grault1 { + new (...args: any[]): void; +} + `, + options: [{ ignoreRestArgs: true }], + }, + { + code: ` +interface Grault2 { + new (...args: readonly any[]): void; +} + `, + options: [{ ignoreRestArgs: true }], + }, + { + code: ` +interface Grault3 { + new (...args: Array): void; +} + `, + options: [{ ignoreRestArgs: true }], + }, + { + code: ` +interface Grault4 { + new (...args: ReadonlyArray): void; +} + `, + options: [{ ignoreRestArgs: true }], + }, + { + code: ` +interface Garply1 { + f(...args: any[]): void; +} + `, + options: [{ ignoreRestArgs: true }], + }, + { + code: ` +interface Garply2 { + f(...args: readonly any[]): void; +} + `, + options: [{ ignoreRestArgs: true }], + }, + { + code: ` +interface Garply3 { + f(...args: Array): void; +} + `, + options: [{ ignoreRestArgs: true }], + }, + { + code: ` +interface Garply4 { + f(...args: ReadonlyArray): void; +} + `, + options: [{ ignoreRestArgs: true }], + }, + { + code: 'declare function waldo1(...args: any[]): void;', + options: [{ ignoreRestArgs: true }], + }, + { + code: 'declare function waldo2(...args: readonly any[]): void;', + options: [{ ignoreRestArgs: true }], + }, + { + code: 'declare function waldo3(...args: Array): void;', + options: [{ ignoreRestArgs: true }], + }, + { + code: 'declare function waldo4(...args: ReadonlyArray): void;', + options: [{ ignoreRestArgs: true }], + }, ], invalid: ([ { @@ -737,7 +849,7 @@ type obj = { ], }, { - code: `class Foo extends Bar {}`, + code: 'class Foo extends Bar {}', errors: [ { messageId: 'unexpectedAny', @@ -746,11 +858,11 @@ type obj = { suggestions: [ { messageId: 'suggestUnknown', - output: `class Foo extends Bar {}`, + output: 'class Foo extends Bar {}', }, { messageId: 'suggestNever', - output: `class Foo extends Bar {}`, + output: 'class Foo extends Bar {}', }, ], }, @@ -761,18 +873,18 @@ type obj = { suggestions: [ { messageId: 'suggestUnknown', - output: `class Foo extends Bar {}`, + output: 'class Foo extends Bar {}', }, { messageId: 'suggestNever', - output: `class Foo extends Bar {}`, + output: 'class Foo extends Bar {}', }, ], }, ], }, { - code: `abstract class Foo extends Bar {}`, + code: 'abstract class Foo extends Bar {}', errors: [ { messageId: 'unexpectedAny', @@ -781,11 +893,11 @@ type obj = { suggestions: [ { messageId: 'suggestUnknown', - output: `abstract class Foo extends Bar {}`, + output: 'abstract class Foo extends Bar {}', }, { messageId: 'suggestNever', - output: `abstract class Foo extends Bar {}`, + output: 'abstract class Foo extends Bar {}', }, ], }, @@ -796,18 +908,18 @@ type obj = { suggestions: [ { messageId: 'suggestUnknown', - output: `abstract class Foo extends Bar {}`, + output: 'abstract class Foo extends Bar {}', }, { messageId: 'suggestNever', - output: `abstract class Foo extends Bar {}`, + output: 'abstract class Foo extends Bar {}', }, ], }, ], }, { - code: `abstract class Foo implements Bar, Baz {}`, + code: 'abstract class Foo implements Bar, Baz {}', errors: [ { messageId: 'unexpectedAny', @@ -816,11 +928,13 @@ type obj = { suggestions: [ { messageId: 'suggestUnknown', - output: `abstract class Foo implements Bar, Baz {}`, + output: + 'abstract class Foo implements Bar, Baz {}', }, { messageId: 'suggestNever', - output: `abstract class Foo implements Bar, Baz {}`, + output: + 'abstract class Foo implements Bar, Baz {}', }, ], }, @@ -831,11 +945,13 @@ type obj = { suggestions: [ { messageId: 'suggestUnknown', - output: `abstract class Foo implements Bar, Baz {}`, + output: + 'abstract class Foo implements Bar, Baz {}', }, { messageId: 'suggestNever', - output: `abstract class Foo implements Bar, Baz {}`, + output: + 'abstract class Foo implements Bar, Baz {}', }, ], }, @@ -846,18 +962,20 @@ type obj = { suggestions: [ { messageId: 'suggestUnknown', - output: `abstract class Foo implements Bar, Baz {}`, + output: + 'abstract class Foo implements Bar, Baz {}', }, { messageId: 'suggestNever', - output: `abstract class Foo implements Bar, Baz {}`, + output: + 'abstract class Foo implements Bar, Baz {}', }, ], }, ], }, { - code: `new Foo()`, + code: 'new Foo()', errors: [ { messageId: 'unexpectedAny', @@ -867,7 +985,7 @@ type obj = { ], }, { - code: `Foo()`, + code: 'Foo()', errors: [ { messageId: 'unexpectedAny', @@ -943,7 +1061,7 @@ const test = >() => {}; ], }, { - code: `type Any = any;`, + code: 'type Any = any;', options: [{ ignoreRestArgs: true }], errors: [ { @@ -954,7 +1072,7 @@ const test = >() => {}; ], }, { - code: `function foo5(...args: any) {}`, + code: 'function foo5(...args: any) {}', options: [{ ignoreRestArgs: true }], errors: [ { @@ -965,7 +1083,7 @@ const test = >() => {}; ], }, { - code: `const bar5 = function (...args: any) {}`, + code: 'const bar5 = function (...args: any) {}', options: [{ ignoreRestArgs: true }], errors: [ { @@ -976,7 +1094,7 @@ const test = >() => {}; ], }, { - code: `const baz5 = (...args: any) => {}`, + code: 'const baz5 = (...args: any) => {}', options: [{ ignoreRestArgs: true }], errors: [ { @@ -987,7 +1105,7 @@ const test = >() => {}; ], }, { - code: `interface Qux5 { (...args: any): void; }`, + code: 'interface Qux5 { (...args: any): void; }', options: [{ ignoreRestArgs: true }], errors: [ { @@ -998,7 +1116,7 @@ const test = >() => {}; ], }, { - code: `function quux5(fn: (...args: any) => void): void {}`, + code: 'function quux5(fn: (...args: any) => void): void {}', options: [{ ignoreRestArgs: true }], errors: [ { @@ -1009,7 +1127,7 @@ const test = >() => {}; ], }, { - code: `function quuz5(): ((...args: any) => void) {}`, + code: 'function quuz5(): ((...args: any) => void) {}', options: [{ ignoreRestArgs: true }], errors: [ { @@ -1019,6 +1137,61 @@ const test = >() => {}; }, ], }, + { + code: 'type Fred5 = (...args: any) => void;', + options: [{ ignoreRestArgs: true }], + errors: [ + { + messageId: 'unexpectedAny', + line: 1, + column: 24, + }, + ], + }, + { + code: 'type Corge5 = new (...args: any) => void;', + options: [{ ignoreRestArgs: true }], + errors: [ + { + messageId: 'unexpectedAny', + line: 1, + column: 29, + }, + ], + }, + { + code: 'interface Grault5 { new (...args: any): void; }', + options: [{ ignoreRestArgs: true }], + errors: [ + { + messageId: 'unexpectedAny', + line: 1, + column: 35, + }, + ], + }, + { + code: 'interface Garply5 { f(...args: any): void; }', + options: [{ ignoreRestArgs: true }], + errors: [ + { + messageId: 'unexpectedAny', + line: 1, + column: 32, + }, + ], + }, + { + code: 'declare function waldo5(...args: any): void;', + options: [{ ignoreRestArgs: true }], + errors: [ + { + messageId: 'unexpectedAny', + line: 1, + column: 34, + }, + ], + }, ] as InvalidTestCase[]).reduce((acc, testCase) => { const suggestions = (code: string): SuggestionOutput[] => [ {