diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index 3524da30859..dfcb5d4a63d 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -32,16 +32,32 @@ export default createRule({ create(context, [mode]) { const sourceCode = context.getSourceCode(); return { - 'VariableDeclarator,PropertyDefinition'( - node: TSESTree.VariableDeclarator | TSESTree.PropertyDefinition, + 'VariableDeclarator,PropertyDefinition,:matches(FunctionDeclaration,FunctionExpression) > AssignmentPattern'( + node: + | TSESTree.VariableDeclarator + | TSESTree.PropertyDefinition + | TSESTree.AssignmentPattern, ): void { - const lhs = ( - node.type === AST_NODE_TYPES.VariableDeclarator ? node.id : node - ).typeAnnotation?.typeAnnotation; - const rhs = - node.type === AST_NODE_TYPES.VariableDeclarator - ? node.init - : node.value; + function getLHSRHS(): [ + TSESTree.BindingName | TSESTree.PropertyDefinition, + TSESTree.Expression | null, + ] { + switch (node.type) { + case AST_NODE_TYPES.VariableDeclarator: + return [node.id, node.init]; + case AST_NODE_TYPES.PropertyDefinition: + return [node, node.value]; + case AST_NODE_TYPES.AssignmentPattern: + return [node.left, node.right]; + default: + throw new Error( + `Unhandled node type: ${(node as { type: string }).type}`, + ); + } + } + const [lhsName, rhs] = getLHSRHS(); + const lhs = lhsName.typeAnnotation?.typeAnnotation; + if ( !rhs || rhs.type !== AST_NODE_TYPES.NewExpression || @@ -69,8 +85,8 @@ export default createRule({ function getIDToAttachAnnotation(): | TSESTree.Token | TSESTree.Node { - if (node.type === AST_NODE_TYPES.VariableDeclarator) { - return node.id; + if (node.type !== AST_NODE_TYPES.PropertyDefinition) { + return lhsName; } if (!node.computed) { return node.key; diff --git a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts index 1ef61f7e55b..6c53d13861c 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -23,6 +23,23 @@ ruleTester.run('consistent-generic-constructors', rule, { class Foo { a = new Foo(); } + `, + ` +function foo(a: Foo = new Foo()) {} + `, + ` +function foo({ a }: Foo = new Foo()) {} + `, + ` +function foo([a]: Foo = new Foo()) {} + `, + ` +class A { + constructor(a: Foo = new Foo()) {} +} + `, + ` +const a = function (a: Foo = new Foo()) {}; `, // type-annotation { @@ -73,6 +90,50 @@ class Foo { `, options: ['type-annotation'], }, + { + code: ` +function foo(a: Foo = new Foo()) {} + `, + options: ['type-annotation'], + }, + { + code: ` +function foo({ a }: Foo = new Foo()) {} + `, + options: ['type-annotation'], + }, + { + code: ` +function foo([a]: Foo = new Foo()) {} + `, + options: ['type-annotation'], + }, + { + code: ` +class A { + constructor(a: Foo = new Foo()) {} +} + `, + options: ['type-annotation'], + }, + { + code: ` +const a = function (a: Foo = new Foo()) {}; + `, + options: ['type-annotation'], + }, + { + code: ` +const [a = new Foo()] = []; + `, + options: ['type-annotation'], + }, + { + code: ` +function a([a = new Foo()]) {} + `, + options: ['type-annotation'], + }, ], invalid: [ { @@ -190,6 +251,75 @@ class Foo { } `, }, + { + code: ` +function foo(a: Foo = new Foo()) {} + `, + errors: [ + { + messageId: 'preferConstructor', + }, + ], + output: ` +function foo(a = new Foo()) {} + `, + }, + { + code: ` +function foo({ a }: Foo = new Foo()) {} + `, + errors: [ + { + messageId: 'preferConstructor', + }, + ], + output: ` +function foo({ a } = new Foo()) {} + `, + }, + { + code: ` +function foo([a]: Foo = new Foo()) {} + `, + errors: [ + { + messageId: 'preferConstructor', + }, + ], + output: ` +function foo([a] = new Foo()) {} + `, + }, + { + code: ` +class A { + constructor(a: Foo = new Foo()) {} +} + `, + errors: [ + { + messageId: 'preferConstructor', + }, + ], + output: ` +class A { + constructor(a = new Foo()) {} +} + `, + }, + { + code: ` +const a = function (a: Foo = new Foo()) {}; + `, + errors: [ + { + messageId: 'preferConstructor', + }, + ], + output: ` +const a = function (a = new Foo()) {}; + `, + }, { code: 'const a = new Foo();', options: ['type-annotation'], @@ -314,5 +444,79 @@ class Foo { } `, }, + { + code: ` +function foo(a = new Foo()) {} + `, + options: ['type-annotation'], + errors: [ + { + messageId: 'preferTypeAnnotation', + }, + ], + output: ` +function foo(a: Foo = new Foo()) {} + `, + }, + { + code: ` +function foo({ a } = new Foo()) {} + `, + options: ['type-annotation'], + errors: [ + { + messageId: 'preferTypeAnnotation', + }, + ], + output: ` +function foo({ a }: Foo = new Foo()) {} + `, + }, + { + code: ` +function foo([a] = new Foo()) {} + `, + options: ['type-annotation'], + errors: [ + { + messageId: 'preferTypeAnnotation', + }, + ], + output: ` +function foo([a]: Foo = new Foo()) {} + `, + }, + { + code: ` +class A { + constructor(a = new Foo()) {} +} + `, + options: ['type-annotation'], + errors: [ + { + messageId: 'preferTypeAnnotation', + }, + ], + output: ` +class A { + constructor(a: Foo = new Foo()) {} +} + `, + }, + { + code: ` +const a = function (a = new Foo()) {}; + `, + options: ['type-annotation'], + errors: [ + { + messageId: 'preferTypeAnnotation', + }, + ], + output: ` +const a = function (a: Foo = new Foo()) {}; + `, + }, ], });