diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index 0df1412bd7b..db95efa9a57 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -1,4 +1,4 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils'; import { createRule } from '../util'; type MessageIds = 'preferTypeAnnotation' | 'preferConstructor'; @@ -30,9 +30,16 @@ export default createRule({ create(context, [mode]) { const sourceCode = context.getSourceCode(); return { - VariableDeclarator(node): void { - const lhs = node.id.typeAnnotation?.typeAnnotation; - const rhs = node.init; + 'VariableDeclarator,PropertyDefinition'( + node: TSESTree.VariableDeclarator | TSESTree.PropertyDefinition, + ): 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; if ( !rhs || rhs.type !== AST_NODE_TYPES.NewExpression || @@ -57,9 +64,25 @@ export default createRule({ node, messageId: 'preferTypeAnnotation', fix(fixer) { + function getIDToAttachAnnotation(): + | TSESTree.Token + | TSESTree.Node { + if (node.type === AST_NODE_TYPES.VariableDeclarator) { + return node.id; + } + if (!node.computed) { + return node.key; + } + // If the property's computed, we have to attach the + // annotation after the square bracket, not the enclosed expression + return sourceCode.getTokenAfter(node.key)!; + } return [ fixer.remove(typeParameters), - fixer.insertTextAfter(node.id, ': ' + typeAnnotation), + fixer.insertTextAfter( + getIDToAttachAnnotation(), + ': ' + typeAnnotation, + ), ]; }, }); 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 0fe3bcae7fb..bdfb68e2788 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -19,6 +19,11 @@ ruleTester.run('consistent-generic-constructors', rule, { 'const a: Foo = Foo();', 'const a: Foo = Foo();', 'const a: Foo = Foo();', + ` +class Foo { + a = new Foo(); +} + `, // type-annotation { code: 'const a = new Foo();', @@ -60,6 +65,14 @@ ruleTester.run('consistent-generic-constructors', rule, { code: 'const a = new (class C {})();', options: ['type-annotation'], }, + { + code: ` +class Foo { + a: Foo = new Foo(); +} + `, + options: ['type-annotation'], + }, ], invalid: [ { @@ -143,6 +156,40 @@ ruleTester.run('consistent-generic-constructors', rule, { ], output: noFormat`const a = new \n Foo \n ();`, }, + { + code: ` +class Foo { + a: Foo = new Foo(); +} + `, + errors: [ + { + messageId: 'preferConstructor', + }, + ], + output: ` +class Foo { + a = new Foo(); +} + `, + }, + { + code: ` +class Foo { + [a]: Foo = new Foo(); +} + `, + errors: [ + { + messageId: 'preferConstructor', + }, + ], + output: ` +class Foo { + [a] = new Foo(); +} + `, + }, { code: 'const a = new Foo();', options: ['type-annotation'], @@ -213,5 +260,59 @@ ruleTester.run('consistent-generic-constructors', rule, { ], output: noFormat`const a: Foo = new Foo();`, }, + { + code: ` +class Foo { + a = new Foo(); +} + `, + options: ['type-annotation'], + errors: [ + { + messageId: 'preferTypeAnnotation', + }, + ], + output: ` +class Foo { + a: Foo = new Foo(); +} + `, + }, + { + code: ` +class Foo { + [a] = new Foo(); +} + `, + options: ['type-annotation'], + errors: [ + { + messageId: 'preferTypeAnnotation', + }, + ], + output: ` +class Foo { + [a]: Foo = new Foo(); +} + `, + }, + { + code: ` +class Foo { + [a + b] = new Foo(); +} + `, + options: ['type-annotation'], + errors: [ + { + messageId: 'preferTypeAnnotation', + }, + ], + output: ` +class Foo { + [a + b]: Foo = new Foo(); +} + `, + }, ], });