From 2d55ffa3b6872c32d40af7c5b59399bc8278bc66 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Wed, 15 Mar 2023 10:07:23 -0700 Subject: [PATCH 1/4] Add suggestions for objectLiteralTypeAssertions --- .../src/rules/consistent-type-assertions.ts | 51 ++++++- .../rules/consistent-type-assertions.test.ts | 132 ++++++++++++++++++ .../typescript-estree/src/version-check.ts | 1 + 3 files changed, 182 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index 66268b0adc67..247c1bbdfab5 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'; @@ -8,7 +8,9 @@ type MessageIds = | 'as' | 'angle-bracket' | 'never' - | 'unexpectedObjectTypeAssertion'; + | 'unexpectedObjectTypeAssertion' + | 'replaceObjectTypeAssertionWithAnnotation' + | 'replaceObjectTypeAssertionWithSatisfies'; type OptUnion = | { assertionStyle: 'as' | 'angle-bracket'; @@ -23,6 +25,7 @@ export default util.createRule({ name: 'consistent-type-assertions', meta: { type: 'suggestion', + hasSuggestions: true, docs: { description: 'Enforce consistent usage of type assertions', recommended: 'strict', @@ -32,6 +35,10 @@ export default util.createRule({ 'angle-bracket': "Use '<{{cast}}>' instead of 'as {{cast}}'.", never: 'Do not use any type assertions.', unexpectedObjectTypeAssertion: 'Always prefer const x: T = { ... }.', + replaceObjectTypeAssertionWithAnnotation: + 'Use const x: {{cast}} = { ... } instead.', + replaceObjectTypeAssertionWithSatisfies: + 'Use const x = { ... } satisfies {{cast}} instead.', }, schema: [ { @@ -148,9 +155,49 @@ export default util.createRule({ checkType(node.typeAnnotation) && node.expression.type === AST_NODE_TYPES.ObjectExpression ) { + const suggest: TSESLint.ReportSuggestionArray = []; + if ( + node.parent && + node.parent.type === AST_NODE_TYPES.VariableDeclarator && + !node.parent.id.typeAnnotation + ) { + const { parent } = node; + suggest.push({ + messageId: 'replaceObjectTypeAssertionWithAnnotation', + data: { cast: sourceCode.getText(node.typeAnnotation) }, + fix: fixer => [ + fixer.insertTextAfter( + parent.id, + `: ${context.getSourceCode().getText(node.typeAnnotation)}`, + ), + fixer.replaceText( + node, + context.getSourceCode().getText(node.expression), + ), + ], + }); + } + suggest.push({ + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: sourceCode.getText(node.typeAnnotation) }, + fix: fixer => [ + fixer.replaceText( + node, + context.getSourceCode().getText(node.expression), + ), + fixer.insertTextAfter( + node, + ` satisfies ${context + .getSourceCode() + .getText(node.typeAnnotation)}`, + ), + ], + }); + context.report({ node, messageId: 'unexpectedObjectTypeAssertion', + suggest, }); } } 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..933a23aae62e 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts @@ -268,6 +268,18 @@ ruleTester.run('consistent-type-assertions', rule, { { messageId: 'unexpectedObjectTypeAssertion', line: 2, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithAnnotation', + data: { cast: 'Foo' }, + output: 'const x: Foo = {};', + }, + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'const x = {} satisfies Foo;', + }, + ], }, ], }), @@ -283,6 +295,18 @@ ruleTester.run('consistent-type-assertions', rule, { { messageId: 'unexpectedObjectTypeAssertion', line: 2, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithAnnotation', + data: { cast: 'Foo' }, + output: 'const x: Foo = {};', + }, + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'const x = {} satisfies Foo;', + }, + ], }, ], }), @@ -298,34 +322,95 @@ ruleTester.run('consistent-type-assertions', rule, { { messageId: 'unexpectedObjectTypeAssertion', line: 2, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithAnnotation', + data: { cast: 'Foo' }, + output: 'const x: Foo = {};', + }, + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'const x = {} satisfies Foo;', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 3, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'print({ bar: 5 } satisfies Foo)', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 4, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'new print({ bar: 5 } satisfies Foo)', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 5, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'function foo() { throw { bar: 5 } satisfies Foo }', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 6, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo.Bar' }, + output: 'function b(x = {} satisfies Foo.Bar) {}', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 7, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'function c(x = {} satisfies Foo) {}', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 8, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'print?.({ bar: 5 } satisfies Foo)', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 9, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'print?.call({ bar: 5 } satisfies Foo)', + }, + ], }, ], }), @@ -341,26 +426,73 @@ ruleTester.run('consistent-type-assertions', rule, { { messageId: 'unexpectedObjectTypeAssertion', line: 2, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithAnnotation', + data: { cast: 'Foo' }, + output: 'const x: Foo = {};', + }, + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'const x = {} satisfies Foo;', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 3, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'print({ bar: 5 } satisfies Foo)', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 4, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'new print({ bar: 5 } satisfies Foo)', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 5, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'function foo() { throw { bar: 5 } satisfies Foo }', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 6, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'print?.({ bar: 5 } satisfies Foo)', + }, + ], }, { messageId: 'unexpectedObjectTypeAssertion', line: 7, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'Foo' }, + output: 'print?.call({ bar: 5 } satisfies Foo)', + }, + ], }, ], }), diff --git a/packages/typescript-estree/src/version-check.ts b/packages/typescript-estree/src/version-check.ts index 194636cb5870..a861635a2406 100644 --- a/packages/typescript-estree/src/version-check.ts +++ b/packages/typescript-estree/src/version-check.ts @@ -24,6 +24,7 @@ const versions = [ '4.6', '4.7', '4.8', + '4.9', ] as const; type Versions = typeof versions extends ArrayLike ? U : never; From 0806103f599a2981b425e673a2c7f6517d7a4853 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Mon, 20 Mar 2023 10:00:52 -0700 Subject: [PATCH 2/4] Add additional test cases --- .../rules/consistent-type-assertions.test.ts | 68 ++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) 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 933a23aae62e..0a547720466c 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,11 @@ 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 = (new Generic()); +const x = (new (Generic)());`; const ANGLE_BRACKET_TESTS = `${ANGLE_BRACKET_TESTS_EXCEPT_CONST_CASE} const x = { key: 'value' }; @@ -19,7 +23,11 @@ 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 = (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 +172,22 @@ 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, + }, ], }), ...batchedSingleLineTests({ @@ -194,6 +218,22 @@ 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, + }, ], }), ...batchedSingleLineTests({ @@ -224,6 +264,18 @@ ruleTester.run('consistent-type-assertions', rule, { messageId: 'never', line: 6, }, + { + messageId: 'never', + line: 7, + }, + { + messageId: 'never', + line: 8, + }, + { + messageId: 'never', + line: 9, + }, ], }), ...batchedSingleLineTests({ @@ -254,6 +306,18 @@ ruleTester.run('consistent-type-assertions', rule, { messageId: 'never', line: 6, }, + { + messageId: 'never', + line: 7, + }, + { + messageId: 'never', + line: 8, + }, + { + messageId: 'never', + line: 9, + }, ], }), ...batchedSingleLineTests({ From a81e39f8f9dca686354ebd83b7134088ecf48447 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Mon, 20 Mar 2023 10:44:39 -0700 Subject: [PATCH 3/4] Capture parentheses --- .../src/rules/consistent-type-assertions.ts | 43 +++- .../rules/consistent-type-assertions.test.ts | 200 +++++++++++------- 2 files changed, 156 insertions(+), 87 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index 247c1bbdfab5..24d926dcc202 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts @@ -128,6 +128,34 @@ export default util.createRule({ } } + function removeAssertionText( + fixer: TSESLint.RuleFixer, + node: TSESTree.TSTypeAssertion | TSESTree.TSAsExpression, + ): TSESLint.RuleFix { + // Capture parentheses before and after the expression + let beforeCount = 0; + let afterCount = 0; + + if (util.isParenthesized(node.expression, sourceCode)) { + const bodyOpeningParen = sourceCode.getTokenBefore( + node.expression, + util.isOpeningParenToken, + )!; + const bodyClosingParen = sourceCode.getTokenAfter( + node.expression, + util.isClosingParenToken, + )!; + + beforeCount = node.expression.range[0] - bodyOpeningParen.range[0]; + afterCount = bodyClosingParen.range[1] - node.expression.range[1]; + } + + return fixer.replaceText( + node, + sourceCode.getText(node.expression, beforeCount, afterCount), + ); + } + function checkExpression( node: TSESTree.TSTypeAssertion | TSESTree.TSAsExpression, ): void { @@ -157,8 +185,7 @@ export default util.createRule({ ) { const suggest: TSESLint.ReportSuggestionArray = []; if ( - node.parent && - node.parent.type === AST_NODE_TYPES.VariableDeclarator && + node.parent?.type === AST_NODE_TYPES.VariableDeclarator && !node.parent.id.typeAnnotation ) { const { parent } = node; @@ -168,12 +195,9 @@ export default util.createRule({ fix: fixer => [ fixer.insertTextAfter( parent.id, - `: ${context.getSourceCode().getText(node.typeAnnotation)}`, - ), - fixer.replaceText( - node, - context.getSourceCode().getText(node.expression), + `: ${sourceCode.getText(node.typeAnnotation)}`, ), + removeAssertionText(fixer, node), ], }); } @@ -181,10 +205,7 @@ export default util.createRule({ messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: sourceCode.getText(node.typeAnnotation) }, fix: fixer => [ - fixer.replaceText( - node, - context.getSourceCode().getText(node.expression), - ), + removeAssertionText(fixer, node), fixer.insertTextAfter( node, ` satisfies ${context 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 0a547720466c..51ee712a0d64 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts @@ -9,11 +9,7 @@ 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 = a + b; -const x = (new Generic()); -const x = (new (Generic)());`; +const x = ('string');`; const ANGLE_BRACKET_TESTS = `${ANGLE_BRACKET_TESTS_EXCEPT_CONST_CASE} const x = { key: 'value' }; @@ -23,11 +19,7 @@ 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; -const x = a as A + b; -const x = (new Generic()) as Foo; -const x = (new (Generic as Foo)());`; +const x = ('string') as a | b;`; const AS_TESTS = `${AS_TESTS_EXCEPT_CONST_CASE} const x = { key: 'value' } as const; @@ -35,9 +27,13 @@ const x = { key: 'value' } as const; const OBJECT_LITERAL_AS_CASTS = ` const x = {} as Foo; +const x = ({}) as a | b; +const x = {} as A + b; `; const OBJECT_LITERAL_ANGLE_BRACKET_CASTS = ` const x = >{}; +const x = ({}); +const x = {} + b; `; const OBJECT_LITERAL_ARGUMENT_AS_CASTS = ` print({ bar: 5 } as Foo) @@ -172,22 +168,6 @@ 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, - }, ], }), ...batchedSingleLineTests({ @@ -218,22 +198,6 @@ 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, - }, ], }), ...batchedSingleLineTests({ @@ -264,18 +228,6 @@ ruleTester.run('consistent-type-assertions', rule, { messageId: 'never', line: 6, }, - { - messageId: 'never', - line: 7, - }, - { - messageId: 'never', - line: 8, - }, - { - messageId: 'never', - line: 9, - }, ], }), ...batchedSingleLineTests({ @@ -306,18 +258,6 @@ ruleTester.run('consistent-type-assertions', rule, { messageId: 'never', line: 6, }, - { - messageId: 'never', - line: 7, - }, - { - messageId: 'never', - line: 8, - }, - { - messageId: 'never', - line: 9, - }, ], }), ...batchedSingleLineTests({ @@ -345,6 +285,33 @@ ruleTester.run('consistent-type-assertions', rule, { }, ], }, + { + messageId: 'unexpectedObjectTypeAssertion', + line: 3, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithAnnotation', + data: { cast: 'a | b' }, + output: 'const x: a | b = ({});', + }, + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'a | b' }, + output: 'const x = ({}) satisfies a | b;', + }, + ], + }, + { + messageId: 'unexpectedObjectTypeAssertion', + line: 4, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'A' }, + output: 'const x = {} satisfies A + b;', + }, + ], + }, ], }), ...batchedSingleLineTests({ @@ -372,6 +339,33 @@ ruleTester.run('consistent-type-assertions', rule, { }, ], }, + { + messageId: 'unexpectedObjectTypeAssertion', + line: 3, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithAnnotation', + data: { cast: 'a | b' }, + output: 'const x: a | b = ({});', + }, + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'a | b' }, + output: 'const x = ({}) satisfies a | b;', + }, + ], + }, + { + messageId: 'unexpectedObjectTypeAssertion', + line: 4, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'A' }, + output: 'const x = {} satisfies A + b;', + }, + ], + }, ], }), ...batchedSingleLineTests({ @@ -402,6 +396,33 @@ ruleTester.run('consistent-type-assertions', rule, { { messageId: 'unexpectedObjectTypeAssertion', line: 3, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithAnnotation', + data: { cast: 'a | b' }, + output: 'const x: a | b = ({});', + }, + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'a | b' }, + output: 'const x = ({}) satisfies a | b;', + }, + ], + }, + { + messageId: 'unexpectedObjectTypeAssertion', + line: 4, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'A' }, + output: 'const x = {} satisfies A + b;', + }, + ], + }, + { + messageId: 'unexpectedObjectTypeAssertion', + line: 5, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', @@ -412,7 +433,7 @@ ruleTester.run('consistent-type-assertions', rule, { }, { messageId: 'unexpectedObjectTypeAssertion', - line: 4, + line: 6, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', @@ -423,7 +444,7 @@ ruleTester.run('consistent-type-assertions', rule, { }, { messageId: 'unexpectedObjectTypeAssertion', - line: 5, + line: 7, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', @@ -434,7 +455,7 @@ ruleTester.run('consistent-type-assertions', rule, { }, { messageId: 'unexpectedObjectTypeAssertion', - line: 6, + line: 8, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', @@ -445,7 +466,7 @@ ruleTester.run('consistent-type-assertions', rule, { }, { messageId: 'unexpectedObjectTypeAssertion', - line: 7, + line: 9, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', @@ -456,7 +477,7 @@ ruleTester.run('consistent-type-assertions', rule, { }, { messageId: 'unexpectedObjectTypeAssertion', - line: 8, + line: 10, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', @@ -467,7 +488,7 @@ ruleTester.run('consistent-type-assertions', rule, { }, { messageId: 'unexpectedObjectTypeAssertion', - line: 9, + line: 11, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', @@ -506,6 +527,33 @@ ruleTester.run('consistent-type-assertions', rule, { { messageId: 'unexpectedObjectTypeAssertion', line: 3, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithAnnotation', + data: { cast: 'a | b' }, + output: 'const x: a | b = ({});', + }, + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'a | b' }, + output: 'const x = ({}) satisfies a | b;', + }, + ], + }, + { + messageId: 'unexpectedObjectTypeAssertion', + line: 4, + suggestions: [ + { + messageId: 'replaceObjectTypeAssertionWithSatisfies', + data: { cast: 'A' }, + output: 'const x = {} satisfies A + b;', + }, + ], + }, + { + messageId: 'unexpectedObjectTypeAssertion', + line: 5, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', @@ -516,7 +564,7 @@ ruleTester.run('consistent-type-assertions', rule, { }, { messageId: 'unexpectedObjectTypeAssertion', - line: 4, + line: 6, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', @@ -527,7 +575,7 @@ ruleTester.run('consistent-type-assertions', rule, { }, { messageId: 'unexpectedObjectTypeAssertion', - line: 5, + line: 7, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', @@ -538,7 +586,7 @@ ruleTester.run('consistent-type-assertions', rule, { }, { messageId: 'unexpectedObjectTypeAssertion', - line: 6, + line: 8, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', @@ -549,7 +597,7 @@ ruleTester.run('consistent-type-assertions', rule, { }, { messageId: 'unexpectedObjectTypeAssertion', - line: 7, + line: 9, suggestions: [ { messageId: 'replaceObjectTypeAssertionWithSatisfies', From 0fdac48da6312c01dd3044e2fa457716fbb12a54 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Mon, 20 Mar 2023 10:56:47 -0700 Subject: [PATCH 4/4] Use changes from other PR --- .../src/rules/consistent-type-assertions.ts | 54 +++++++++---------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index 24d926dcc202..8bf1c8356473 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts @@ -90,6 +90,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 { @@ -128,34 +150,6 @@ export default util.createRule({ } } - function removeAssertionText( - fixer: TSESLint.RuleFixer, - node: TSESTree.TSTypeAssertion | TSESTree.TSAsExpression, - ): TSESLint.RuleFix { - // Capture parentheses before and after the expression - let beforeCount = 0; - let afterCount = 0; - - if (util.isParenthesized(node.expression, sourceCode)) { - const bodyOpeningParen = sourceCode.getTokenBefore( - node.expression, - util.isOpeningParenToken, - )!; - const bodyClosingParen = sourceCode.getTokenAfter( - node.expression, - util.isClosingParenToken, - )!; - - beforeCount = node.expression.range[0] - bodyOpeningParen.range[0]; - afterCount = bodyClosingParen.range[1] - node.expression.range[1]; - } - - return fixer.replaceText( - node, - sourceCode.getText(node.expression, beforeCount, afterCount), - ); - } - function checkExpression( node: TSESTree.TSTypeAssertion | TSESTree.TSAsExpression, ): void { @@ -197,7 +191,7 @@ export default util.createRule({ parent.id, `: ${sourceCode.getText(node.typeAnnotation)}`, ), - removeAssertionText(fixer, node), + fixer.replaceText(node, getTextWithParentheses(node.expression)), ], }); } @@ -205,7 +199,7 @@ export default util.createRule({ messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: sourceCode.getText(node.typeAnnotation) }, fix: fixer => [ - removeAssertionText(fixer, node), + fixer.replaceText(node, getTextWithParentheses(node.expression)), fixer.insertTextAfter( node, ` satisfies ${context