From e53dde031cecca773742fd4ef58b9dc147825df3 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Wed, 15 Mar 2023 10:28:20 -0700 Subject: [PATCH 1/2] Autofix angle bracket assertions to as --- .../src/rules/consistent-type-assertions.ts | 16 +++++++++++++++- .../rules/consistent-type-assertions.test.ts | 6 ++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index 66268b0adc67..954f4426004e 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', @@ -100,6 +101,19 @@ export default util.createRule({ messageId !== 'never' ? { cast: sourceCode.getText(node.typeAnnotation) } : {}, + fix: + messageId === 'as' + ? (fixer): TSESLint.RuleFix[] => [ + fixer.replaceText( + node, + context.getSourceCode().getText(node.expression), + ), + fixer.insertTextAfter( + node, + ` as ${context.getSourceCode().getText(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..6ec7ac89f763 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts @@ -195,6 +195,12 @@ ruleTester.run('consistent-type-assertions', rule, { line: 6, }, ], + output: ` +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 = { key: 'value' } as const;`, }), ...batchedSingleLineTests({ code: AS_TESTS_EXCEPT_CONST_CASE, From 631994b584df79b0fd823bdd41362c33ba35a428 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Mon, 20 Mar 2023 10:55:20 -0700 Subject: [PATCH 2/2] Include parentheses --- .../src/rules/consistent-type-assertions.ts | 26 +++++- .../rules/consistent-type-assertions.test.ts | 93 +++++++++++++++++-- 2 files changed, 109 insertions(+), 10 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index 954f4426004e..544b78e177fd 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts @@ -84,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 { @@ -106,11 +128,11 @@ export default util.createRule({ ? (fixer): TSESLint.RuleFix[] => [ fixer.replaceText( node, - context.getSourceCode().getText(node.expression), + getTextWithParentheses(node.expression), ), fixer.insertTextAfter( node, - ` as ${context.getSourceCode().getText(node.typeAnnotation)}`, + ` 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 6ec7ac89f763..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,13 +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: ` -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 = { key: 'value' } as const;`, + output: AS_TESTS, }), ...batchedSingleLineTests({ code: AS_TESTS_EXCEPT_CONST_CASE, @@ -230,6 +275,22 @@ const x = { key: 'value' } as const;`, messageId: 'never', line: 6, }, + { + messageId: 'never', + line: 7, + }, + { + messageId: 'never', + line: 8, + }, + { + messageId: 'never', + line: 9, + }, + { + messageId: 'never', + line: 10, + }, ], }), ...batchedSingleLineTests({ @@ -260,6 +321,22 @@ const x = { key: 'value' } as const;`, messageId: 'never', line: 6, }, + { + messageId: 'never', + line: 7, + }, + { + messageId: 'never', + line: 8, + }, + { + messageId: 'never', + line: 9, + }, + { + messageId: 'never', + line: 10, + }, ], }), ...batchedSingleLineTests({