From 141bde575a6caecd280c3ce279bbf18b4ad0cd57 Mon Sep 17 00:00:00 2001 From: auvred Date: Wed, 31 Jan 2024 18:49:56 +0300 Subject: [PATCH 1/2] fix(eslint-plugin): [no-unnecessary-type-assertion] provide valid fixes for assertions with extra tokens before `as` keyword --- .../rules/no-unnecessary-type-assertion.ts | 20 ++-- .../no-unnecessary-type-assertion.test.ts | 97 ++++++++++++++++++- 2 files changed, 108 insertions(+), 9 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts index 58ef344bf515..804eaf9b3516 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -1,5 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import { getSourceCode } from '@typescript-eslint/utils/eslint-utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -273,13 +273,17 @@ export default createRule({ ]) : null; } - return fixer.removeRange([ - node.expression.range[1] + - (node.expression.type === AST_NODE_TYPES.CallExpression - ? 0 - : 1), - node.range[1], - ]); + // `as` is always present in TSAsExpression + const asToken = sourceCode.getTokenAfter( + node.expression, + token => + token.type === AST_TOKEN_TYPES.Identifier && + token.value === 'as', + )!; + const tokenBeforeAs = sourceCode.getTokenBefore(asToken, { + includeComments: true, + })!; + return fixer.removeRange([tokenBeforeAs.range[1], node.range[1]]); }, }); } diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts index 2c3cc3599e83..d018ab0c2bd4 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts @@ -1,4 +1,4 @@ -import { RuleTester } from '@typescript-eslint/rule-tester'; +import { noFormat, RuleTester } from '@typescript-eslint/rule-tester'; import path from 'path'; import rule from '../../src/rules/no-unnecessary-type-assertion'; @@ -660,5 +660,100 @@ const item = arr[0]; }, ], }, + { + code: noFormat` +const foo = ( 3 + 5 ) as number; + `, + output: ` +const foo = ( 3 + 5 ); + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + column: 13, + }, + ], + }, + { + code: noFormat` +const foo = ( 3 + 5 ) /*as*/ as number; + `, + output: ` +const foo = ( 3 + 5 ) /*as*/; + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + column: 13, + }, + ], + }, + { + code: noFormat` +const foo = ( 3 + 5 + ) /*as*/ as //as + ( + number + ); + `, + output: ` +const foo = ( 3 + 5 + ) /*as*/; + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + column: 13, + }, + ], + }, + { + code: noFormat` +const foo = (3 + (5 as number) ) as number; + `, + output: ` +const foo = (3 + (5 as number) ); + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + column: 13, + }, + ], + }, + { + code: noFormat` +const foo = 3 + 5/*as*/ as number; + `, + output: ` +const foo = 3 + 5/*as*/; + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + column: 13, + }, + ], + }, + { + code: noFormat` +const foo = 3 + 5/*a*/ /*b*/ as number; + `, + output: ` +const foo = 3 + 5/*a*/ /*b*/; + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + column: 13, + }, + ], + }, ], }); From 331ed570f19438982f7d761d36f92dc18653cc5c Mon Sep 17 00:00:00 2001 From: auvred Date: Wed, 31 Jan 2024 19:07:46 +0300 Subject: [PATCH 2/2] fix: provide fixes for angle bracket assertions when there're tokens inside --- .../rules/no-unnecessary-type-assertion.ts | 25 +++++--- .../no-unnecessary-type-assertion.test.ts | 60 +++++++++++++++++++ 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts index 804eaf9b3516..ea17c721404b 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -263,15 +263,24 @@ export default createRule({ messageId: 'unnecessaryAssertion', fix(fixer) { if (node.type === AST_NODE_TYPES.TSTypeAssertion) { + const openingAngleBracket = sourceCode.getTokenBefore( + node.typeAnnotation, + token => + token.type === AST_TOKEN_TYPES.Punctuator && + token.value === '<', + )!; const closingAngleBracket = sourceCode.getTokenAfter( node.typeAnnotation, - ); - return closingAngleBracket?.value === '>' - ? fixer.removeRange([ - node.range[0], - closingAngleBracket.range[1], - ]) - : null; + token => + token.type === AST_TOKEN_TYPES.Punctuator && + token.value === '>', + )!; + // < ( number ) > ( 3 + 5 ) + // ^---remove---^ + return fixer.removeRange([ + openingAngleBracket.range[0], + closingAngleBracket.range[1], + ]); } // `as` is always present in TSAsExpression const asToken = sourceCode.getTokenAfter( @@ -283,6 +292,8 @@ export default createRule({ const tokenBeforeAs = sourceCode.getTokenBefore(asToken, { includeComments: true, })!; + // ( 3 + 5 ) as number + // ^--remove--^ return fixer.removeRange([tokenBeforeAs.range[1], node.range[1]]); }, }); diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts index d018ab0c2bd4..3eb36636a839 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts @@ -755,5 +755,65 @@ const foo = 3 + 5/*a*/ /*b*/; }, ], }, + { + code: noFormat` +const foo = <(number)>(3 + 5); + `, + output: ` +const foo = (3 + 5); + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + column: 13, + }, + ], + }, + { + code: noFormat` +const foo = < ( number ) >( 3 + 5 ); + `, + output: ` +const foo = ( 3 + 5 ); + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + column: 13, + }, + ], + }, + { + code: noFormat` +const foo = /* a */ (3 + 5); + `, + output: ` +const foo = /* a */ (3 + 5); + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + column: 13, + }, + ], + }, + { + code: noFormat` +const foo = (3 + 5); + `, + output: ` +const foo = (3 + 5); + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + column: 13, + }, + ], + }, ], });