diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index 66268b0adc67..544b78e177fd 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts @@ -1,4 +1,4 @@ -import type { TSESTree } from '@typescript-eslint/utils'; +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as util from '../util'; @@ -23,6 +23,7 @@ export default util.createRule({ name: 'consistent-type-assertions', meta: { type: 'suggestion', + fixable: 'code', docs: { description: 'Enforce consistent usage of type assertions', recommended: 'strict', @@ -83,6 +84,28 @@ export default util.createRule({ ); } + function getTextWithParentheses(node: TSESTree.Node): string { + // Capture parentheses before and after the node + let beforeCount = 0; + let afterCount = 0; + + if (util.isParenthesized(node, sourceCode)) { + const bodyOpeningParen = sourceCode.getTokenBefore( + node, + util.isOpeningParenToken, + )!; + const bodyClosingParen = sourceCode.getTokenAfter( + node, + util.isClosingParenToken, + )!; + + beforeCount = node.range[0] - bodyOpeningParen.range[0]; + afterCount = bodyClosingParen.range[1] - node.range[1]; + } + + return sourceCode.getText(node, beforeCount, afterCount); + } + function reportIncorrectAssertionType( node: TSESTree.TSTypeAssertion | TSESTree.TSAsExpression, ): void { @@ -100,6 +123,19 @@ export default util.createRule({ messageId !== 'never' ? { cast: sourceCode.getText(node.typeAnnotation) } : {}, + fix: + messageId === 'as' + ? (fixer): TSESLint.RuleFix[] => [ + fixer.replaceText( + node, + getTextWithParentheses(node.expression), + ), + fixer.insertTextAfter( + node, + ` as ${getTextWithParentheses(node.typeAnnotation)}`, + ), + ] + : undefined, }); } diff --git a/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts b/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts index 98fcdc026059..d8d61749d87a 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts @@ -9,7 +9,12 @@ const ANGLE_BRACKET_TESTS_EXCEPT_CONST_CASE = ` const x = new Generic(); const x = b; const x = [1]; -const x = ('string');`; +const x = ('string'); +const x = !'string'; +const x = a + b; +const x = <(A)>a + (b); +const x = (new Generic()); +const x = (new (Generic)());`; const ANGLE_BRACKET_TESTS = `${ANGLE_BRACKET_TESTS_EXCEPT_CONST_CASE} const x = { key: 'value' }; @@ -19,7 +24,12 @@ const AS_TESTS_EXCEPT_CONST_CASE = ` const x = new Generic() as Foo; const x = b as A; const x = [1] as readonly number[]; -const x = ('string') as a | b;`; +const x = ('string') as a | b; +const x = !'string' as A; +const x = a as A + b; +const x = a as (A) + (b); +const x = (new Generic()) as Foo; +const x = (new (Generic as Foo)());`; const AS_TESTS = `${AS_TESTS_EXCEPT_CONST_CASE} const x = { key: 'value' } as const; @@ -164,6 +174,26 @@ ruleTester.run('consistent-type-assertions', rule, { messageId: 'angle-bracket', line: 6, }, + { + messageId: 'angle-bracket', + line: 7, + }, + { + messageId: 'angle-bracket', + line: 8, + }, + { + messageId: 'angle-bracket', + line: 9, + }, + { + messageId: 'angle-bracket', + line: 10, + }, + { + messageId: 'angle-bracket', + line: 11, + }, ], }), ...batchedSingleLineTests({ @@ -194,7 +224,28 @@ ruleTester.run('consistent-type-assertions', rule, { messageId: 'as', line: 6, }, + { + messageId: 'as', + line: 7, + }, + { + messageId: 'as', + line: 8, + }, + { + messageId: 'as', + line: 9, + }, + { + messageId: 'as', + line: 10, + }, + { + messageId: 'as', + line: 11, + }, ], + output: AS_TESTS, }), ...batchedSingleLineTests({ code: AS_TESTS_EXCEPT_CONST_CASE, @@ -224,6 +275,22 @@ ruleTester.run('consistent-type-assertions', rule, { messageId: 'never', line: 6, }, + { + messageId: 'never', + line: 7, + }, + { + messageId: 'never', + line: 8, + }, + { + messageId: 'never', + line: 9, + }, + { + messageId: 'never', + line: 10, + }, ], }), ...batchedSingleLineTests({ @@ -254,6 +321,22 @@ ruleTester.run('consistent-type-assertions', rule, { messageId: 'never', line: 6, }, + { + messageId: 'never', + line: 7, + }, + { + messageId: 'never', + line: 8, + }, + { + messageId: 'never', + line: 9, + }, + { + messageId: 'never', + line: 10, + }, ], }), ...batchedSingleLineTests({