From d56e6f2b9bea1d90c0ea6bbfb420c737a3d2d07e Mon Sep 17 00:00:00 2001 From: StyleShit Date: Sun, 19 Nov 2023 21:13:19 +0200 Subject: [PATCH 01/17] feat(eslint-plugin): [no-useless-template-literals] add new rule Closes #2846 --- .../rules/no-useless-template-literals.md | 64 ++++++ .../src/rules/no-useless-template-literals.ts | 90 ++++++++ .../no-useless-template-literals.test.ts | 194 ++++++++++++++++++ 3 files changed, 348 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/no-useless-template-literals.md create mode 100644 packages/eslint-plugin/src/rules/no-useless-template-literals.ts create mode 100644 packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts diff --git a/packages/eslint-plugin/docs/rules/no-useless-template-literals.md b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md new file mode 100644 index 000000000000..d85520dfb900 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md @@ -0,0 +1,64 @@ +--- +description: 'Disallow unnecessary template literals.' +--- + +> 🛑 This file is source code, not the primary documentation location! 🛑 +> +> See **https://typescript-eslint.io/rules/no-useless-template-literals** for documentation. + +This rule reports template literals that can be simplified to a normal string literal. + +## Examples + + + +### ❌ Incorrect + +```ts +const ab1 = `${'a'}${'b'}`; +const ab2 = `a${'b'}`; + +const stringWithNumber = `1 + 1 = ${2}`; + +const stringWithBoolean = `${'true is '}${true}`; + +const string = 'a'; +const wrappedString = `${string}`; + +declare const intersectionWithString: string & { _brand: 'test-brand' }; +const wrappedIntersection = `${intersectionWithString}`; +``` + +### ✅ Correct + +```ts +const string = 'a'; +const concatenatedString = `${string}-b`; + +const number = 1; +const concatenatedNumber = `${number}-2`; + +const boolean = true; +const concatenatedBoolean = `${boolean}-false`; + +const nullish = null; +const concatenatedNullish = `${nullish}-undefined`; + +const left = 'left'; +const right = 'right'; +const concatenatedVariables = `${left}-${right}`; + +const concatenatedExpressions = `${1 + 2}-${3 + 4}`; + +const taggedTemplate = tag`${'a'}-${'b'}`; + +const wrappedNumber = `${number}`; +const wrappedBoolean = `${boolean}`; +const wrappedNull = `${nullish}`; +``` + + + +## Related To + +- [`restrict-template-expressions`](./restrict-template-expressions.md) diff --git a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts new file mode 100644 index 000000000000..8cf39e8668fb --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -0,0 +1,90 @@ +import type { TSESTree } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; +import * as ts from 'typescript'; + +import { + createRule, + getConstrainedTypeAtLocation, + getParserServices, + isTypeFlagSet, +} from '../util'; + +type MessageId = 'noUselessTemplateLiteral'; + +export default createRule<[], MessageId>({ + name: 'no-useless-template-literals', + meta: { + type: 'problem', + docs: { + description: 'Disallow unnecessary template literals', + recommended: 'recommended', + requiresTypeChecking: true, + }, + messages: { + noUselessTemplateLiteral: + 'Template literal expression is unnecessary and can be simplified.', + }, + schema: [], + }, + defaultOptions: [], + create(context) { + const services = getParserServices(context); + + function isUnderlyingTypeString(expression: TSESTree.Expression): boolean { + const type = getConstrainedTypeAtLocation(services, expression); + + const isString = (t: ts.Type): boolean => { + return isTypeFlagSet(t, ts.TypeFlags.StringLike); + }; + + if (type.isUnion()) { + return type.types.every(isString); + } + + if (type.isIntersection()) { + return type.types.some(isString); + } + + return isString(type); + } + + return { + TemplateLiteral(node: TSESTree.TemplateLiteral): void { + // don't check tagged template literals + if (node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) { + return; + } + + // don't allow a single variable in a template literal + const hasSingleStringVariable = + node.quasis.length === 2 && + node.quasis[0].value.raw === '' && + node.quasis[1].value.raw === '' && + node.expressions.length === 1 && + node.expressions[0].type === AST_NODE_TYPES.Identifier && + isUnderlyingTypeString(node.expressions[0]); + + if (hasSingleStringVariable) { + context.report({ + node, + messageId: 'noUselessTemplateLiteral', + }); + + return; + } + + // don't allow concatenating only literals in a template literal + const allAreLiterals = node.expressions.every(expression => { + return expression.type === AST_NODE_TYPES.Literal; + }); + + if (allAreLiterals) { + context.report({ + node, + messageId: 'noUselessTemplateLiteral', + }); + } + }, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts new file mode 100644 index 000000000000..e4ed6d58f263 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts @@ -0,0 +1,194 @@ +import { RuleTester } from '@typescript-eslint/rule-tester'; + +import rule from '../../src/rules/no-useless-template-literals'; +import { getFixturesRootDir } from '../RuleTester'; + +const rootPath = getFixturesRootDir(); + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', + parserOptions: { + tsconfigRootDir: rootPath, + project: './tsconfig.json', + }, +}); + +ruleTester.run('no-useless-template-literals', rule, { + valid: [ + "const string = 'a';", + + // allow variables & literals concatenation + ` + const string = 'a'; + const concatenated = \`\${string}b\`; + `, + + ` + const number = 1; + const concatenated = \`\${number}b\`; + `, + + ` + const boolean = true; + const concatenated = \`\${boolean}b\`; + `, + + ` + const nullish = nullish; + const concatenated = \`\${nullish}-undefined\`; + `, + + ` + const left = 'a'; + const right = 'b'; + const concatenated = \`\${left}\${right}\`; + `, + + ` + const left = 'a'; + const right = 'c'; + const concatenated = \`\${left}b\${right}\`; + `, + + ` + const left = 'a'; + const center = 'b'; + const right = 'c'; + const concatenated = \`\${left}\${center}\${right}\`; + `, + + // allow expressions + ` + const concatenated = \`1 + 1 = \${1 + 1}\`; + `, + + ` + const concatenated = \`true && false = \${true && false}\`; + `, + + // allow tagged template literals + ` + tag\`\${'a'}\${'b'}\`; + `, + + // allow wrapping numbers and booleans since it converts them to strings + ` + const number = 1; + const wrapped = \`\${number}\`; + `, + + ` + const boolean = true; + const wrapped = \`\${boolean}\`; + `, + + ` + const nullish = null; + const wrapped = \`\${nullish}\`; + `, + + // allow union types that include string + ` + declare const union: string | number; + const wrapped = \`\${union}\`; + `, + ], + + invalid: [ + // don't allow concatenating only literals in a template literal + { + code: ` + const concatenated = \`\${'a'}\${'b'}\`; + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 2, + column: 30, + }, + ], + }, + + { + code: ` + const concatenated = \`a\${'b'}\`; + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 2, + column: 30, + }, + ], + }, + + { + code: ` + const concatenated = \`\${'1 + 1 = '}\${2}\`; + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 2, + column: 30, + }, + ], + }, + + { + code: ` + const concatenated = \`1 + 1 = \${2}\`; + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 2, + column: 30, + }, + ], + }, + + { + code: ` + const concatenated = \`\${'a'}\${true}\`; + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 2, + column: 30, + }, + ], + }, + + // don't allow a single string variable in a template literal + { + code: ` + const string = 'a'; + const wrapped = \`\${string}\`; + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 3, + column: 25, + }, + ], + }, + + // don't allow intersection types that include string + { + code: ` + declare const intersection: string & { _brand: 'test-brand' }; + const wrapped = \`\${intersection}\`; + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 3, + column: 25, + }, + ], + }, + ], +}); From 5affb9e51b94c7bc49438b053b32680d153a3d3b Mon Sep 17 00:00:00 2001 From: StyleShit Date: Sun, 19 Nov 2023 21:24:15 +0200 Subject: [PATCH 02/17] fix tests --- packages/eslint-plugin/src/rules/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 5423a7b82075..14c171af990e 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -93,6 +93,7 @@ import noUnusedVars from './no-unused-vars'; import noUseBeforeDefine from './no-use-before-define'; import noUselessConstructor from './no-useless-constructor'; import noUselessEmptyExport from './no-useless-empty-export'; +import noUselessTemplateLiterals from './no-useless-template-literals'; import noVarRequires from './no-var-requires'; import nonNullableTypeAssertionStyle from './non-nullable-type-assertion-style'; import objectCurlySpacing from './object-curly-spacing'; @@ -231,6 +232,7 @@ export default { 'no-use-before-define': noUseBeforeDefine, 'no-useless-constructor': noUselessConstructor, 'no-useless-empty-export': noUselessEmptyExport, + 'no-useless-template-literals': noUselessTemplateLiterals, 'no-var-requires': noVarRequires, 'non-nullable-type-assertion-style': nonNullableTypeAssertionStyle, 'object-curly-spacing': objectCurlySpacing, From 77593acf00cd3513c9c13d72c0c89a38d57d7a57 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Mon, 20 Nov 2023 19:10:34 +0200 Subject: [PATCH 03/17] thank you josh --- .../rules/no-useless-template-literals.md | 27 ++++------- .../src/rules/no-useless-template-literals.ts | 3 -- .../no-useless-template-literals.test.ts | 46 +++++++++++++++---- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-useless-template-literals.md b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md index d85520dfb900..4b2dae0d2879 100644 --- a/packages/eslint-plugin/docs/rules/no-useless-template-literals.md +++ b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md @@ -32,29 +32,18 @@ const wrappedIntersection = `${intersectionWithString}`; ### ✅ Correct ```ts -const string = 'a'; -const concatenatedString = `${string}-b`; - -const number = 1; -const concatenatedNumber = `${number}-2`; - -const boolean = true; -const concatenatedBoolean = `${boolean}-false`; +const ab1 = 'ab'; +const ab2 = 'ab'; -const nullish = null; -const concatenatedNullish = `${nullish}-undefined`; +const stringWithNumber = '1 + 1 = 2'; -const left = 'left'; -const right = 'right'; -const concatenatedVariables = `${left}-${right}`; +const stringWithBoolean = 'true is true'; -const concatenatedExpressions = `${1 + 2}-${3 + 4}`; - -const taggedTemplate = tag`${'a'}-${'b'}`; +const string = 'a'; +const wrappedString = string; -const wrappedNumber = `${number}`; -const wrappedBoolean = `${boolean}`; -const wrappedNull = `${nullish}`; +declare const intersectionWithString: string & { _brand: 'test-brand' }; +const wrappedIntersection = intersectionWithString; ``` diff --git a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts index 8cf39e8668fb..8ba152bbad64 100644 --- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -50,12 +50,10 @@ export default createRule<[], MessageId>({ return { TemplateLiteral(node: TSESTree.TemplateLiteral): void { - // don't check tagged template literals if (node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) { return; } - // don't allow a single variable in a template literal const hasSingleStringVariable = node.quasis.length === 2 && node.quasis[0].value.raw === '' && @@ -73,7 +71,6 @@ export default createRule<[], MessageId>({ return; } - // don't allow concatenating only literals in a template literal const allAreLiterals = node.expressions.every(expression => { return expression.type === AST_NODE_TYPES.Literal; }); diff --git a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts index e4ed6d58f263..308f2d543163 100644 --- a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts +++ b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts @@ -17,7 +17,6 @@ ruleTester.run('no-useless-template-literals', rule, { valid: [ "const string = 'a';", - // allow variables & literals concatenation ` const string = 'a'; const concatenated = \`\${string}b\`; @@ -34,7 +33,7 @@ ruleTester.run('no-useless-template-literals', rule, { `, ` - const nullish = nullish; + const nullish = null; const concatenated = \`\${nullish}-undefined\`; `, @@ -57,7 +56,6 @@ ruleTester.run('no-useless-template-literals', rule, { const concatenated = \`\${left}\${center}\${right}\`; `, - // allow expressions ` const concatenated = \`1 + 1 = \${1 + 1}\`; `, @@ -66,12 +64,10 @@ ruleTester.run('no-useless-template-literals', rule, { const concatenated = \`true && false = \${true && false}\`; `, - // allow tagged template literals ` tag\`\${'a'}\${'b'}\`; `, - // allow wrapping numbers and booleans since it converts them to strings ` const number = 1; const wrapped = \`\${number}\`; @@ -87,15 +83,34 @@ ruleTester.run('no-useless-template-literals', rule, { const wrapped = \`\${nullish}\`; `, - // allow union types that include string ` declare const union: string | number; const wrapped = \`\${union}\`; `, + + ` + declare const unknown: unknown; + const wrapped = \`\${unknown}\`; + `, + + ` + declare const never: never; + const wrapped = \`\${never}\`; + `, + + ` + declare const any: any; + const wrapped = \`\${any}\`; + `, + + ` + function func(arg: T) { + const wrapped = \`\${arg}\`; + } + `, ], invalid: [ - // don't allow concatenating only literals in a template literal { code: ` const concatenated = \`\${'a'}\${'b'}\`; @@ -161,7 +176,6 @@ ruleTester.run('no-useless-template-literals', rule, { ], }, - // don't allow a single string variable in a template literal { code: ` const string = 'a'; @@ -176,7 +190,6 @@ ruleTester.run('no-useless-template-literals', rule, { ], }, - // don't allow intersection types that include string { code: ` declare const intersection: string & { _brand: 'test-brand' }; @@ -190,5 +203,20 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + + { + code: ` + function func(arg: T) { + const wrapped = \`\${arg}\`; + } + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 3, + column: 27, + }, + ], + }, ], }); From d924265faf11ab252b366c0fa3366d20540ebbcd Mon Sep 17 00:00:00 2001 From: StyleShit Date: Mon, 20 Nov 2023 19:14:03 +0200 Subject: [PATCH 04/17] hopefully fix tests? --- packages/eslint-plugin/src/configs/all.ts | 1 + .../src/configs/disable-type-checked.ts | 1 + .../src/configs/recommended-type-checked.ts | 1 + .../src/configs/strict-type-checked.ts | 1 + .../no-useless-template-literals.shot | 14 ++++++++++++++ 5 files changed, 18 insertions(+) create mode 100644 packages/eslint-plugin/tests/schema-snapshots/no-useless-template-literals.shot diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index 7717b386cc9f..9bb19cfd8439 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -135,6 +135,7 @@ export = { 'no-useless-constructor': 'off', '@typescript-eslint/no-useless-constructor': 'error', '@typescript-eslint/no-useless-empty-export': 'error', + '@typescript-eslint/no-useless-template-literals': 'error', '@typescript-eslint/no-var-requires': 'error', '@typescript-eslint/non-nullable-type-assertion-style': 'error', 'object-curly-spacing': 'off', diff --git a/packages/eslint-plugin/src/configs/disable-type-checked.ts b/packages/eslint-plugin/src/configs/disable-type-checked.ts index 38a7ffd079d8..1fa497e0c6d8 100644 --- a/packages/eslint-plugin/src/configs/disable-type-checked.ts +++ b/packages/eslint-plugin/src/configs/disable-type-checked.ts @@ -34,6 +34,7 @@ export = { '@typescript-eslint/no-unsafe-enum-comparison': 'off', '@typescript-eslint/no-unsafe-member-access': 'off', '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-useless-template-literals': 'off', '@typescript-eslint/non-nullable-type-assertion-style': 'off', '@typescript-eslint/prefer-includes': 'off', '@typescript-eslint/prefer-nullish-coalescing': 'off', diff --git a/packages/eslint-plugin/src/configs/recommended-type-checked.ts b/packages/eslint-plugin/src/configs/recommended-type-checked.ts index ab0f50394612..46ac6ee4b3bd 100644 --- a/packages/eslint-plugin/src/configs/recommended-type-checked.ts +++ b/packages/eslint-plugin/src/configs/recommended-type-checked.ts @@ -41,6 +41,7 @@ export = { '@typescript-eslint/no-unsafe-return': 'error', 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/no-useless-template-literals': 'error', '@typescript-eslint/no-var-requires': 'error', '@typescript-eslint/prefer-as-const': 'error', 'require-await': 'off', diff --git a/packages/eslint-plugin/src/configs/strict-type-checked.ts b/packages/eslint-plugin/src/configs/strict-type-checked.ts index dfba0b81c7fa..471175b9bba7 100644 --- a/packages/eslint-plugin/src/configs/strict-type-checked.ts +++ b/packages/eslint-plugin/src/configs/strict-type-checked.ts @@ -56,6 +56,7 @@ export = { '@typescript-eslint/no-unused-vars': 'error', 'no-useless-constructor': 'off', '@typescript-eslint/no-useless-constructor': 'error', + '@typescript-eslint/no-useless-template-literals': 'error', '@typescript-eslint/no-var-requires': 'error', '@typescript-eslint/prefer-as-const': 'error', '@typescript-eslint/prefer-includes': 'error', diff --git a/packages/eslint-plugin/tests/schema-snapshots/no-useless-template-literals.shot b/packages/eslint-plugin/tests/schema-snapshots/no-useless-template-literals.shot new file mode 100644 index 000000000000..785d465a8408 --- /dev/null +++ b/packages/eslint-plugin/tests/schema-snapshots/no-useless-template-literals.shot @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Rule schemas should be convertible to TS types for documentation purposes no-useless-template-literals 1`] = ` +" +# SCHEMA: + +[] + + +# TYPES: + +/** No options declared */ +type Options = [];" +`; From 5e895152d9f580f69401d185d339ad519c56bf6e Mon Sep 17 00:00:00 2001 From: StyleShit Date: Mon, 20 Nov 2023 19:49:14 +0200 Subject: [PATCH 05/17] support template literals with new lines --- .../src/rules/no-useless-template-literals.ts | 8 ++++++++ .../no-useless-template-literals.test.ts | 20 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts index 8ba152bbad64..c511fde7b246 100644 --- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -71,6 +71,14 @@ export default createRule<[], MessageId>({ return; } + const hasStringWithNewLine = node.quasis.some(quasi => { + return /(\r|\n)/.test(quasi.value.raw); + }); + + if (hasStringWithNewLine) { + return; + } + const allAreLiterals = node.expressions.every(expression => { return expression.type === AST_NODE_TYPES.Literal; }); diff --git a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts index 308f2d543163..f19fae12c835 100644 --- a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts +++ b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts @@ -1,4 +1,4 @@ -import { RuleTester } from '@typescript-eslint/rule-tester'; +import { noFormat, RuleTester } from '@typescript-eslint/rule-tester'; import rule from '../../src/rules/no-useless-template-literals'; import { getFixturesRootDir } from '../RuleTester'; @@ -108,6 +108,24 @@ ruleTester.run('no-useless-template-literals', rule, { const wrapped = \`\${arg}\`; } `, + + ` + const wrapped = \`with + + new line\`; + `, + + ` + const a = 'a'; + + const wrapped = \`\${a} with + + new line\`; + `, + + noFormat` + const wrapped = \`with windows \r new line\`; + `, ], invalid: [ From ba23199f38595791451bfbb9594b6c5781eeba81 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Mon, 20 Nov 2023 19:59:03 +0200 Subject: [PATCH 06/17] support also quotes --- .../src/rules/no-useless-template-literals.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts index c511fde7b246..c4e51e6091ab 100644 --- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -71,11 +71,13 @@ export default createRule<[], MessageId>({ return; } - const hasStringWithNewLine = node.quasis.some(quasi => { - return /(\r|\n)/.test(quasi.value.raw); + const allowedChars = ['\r', '\n', "'", '"']; + + const hasStringWithAllowedChars = node.quasis.some(quasi => { + return new RegExp(`[${allowedChars.join('')}]`).test(quasi.value.raw); }); - if (hasStringWithNewLine) { + if (hasStringWithAllowedChars) { return; } From dd17ad16b74c2472f7ac18313113630d72a5e9de Mon Sep 17 00:00:00 2001 From: StyleShit Date: Mon, 20 Nov 2023 20:27:26 +0200 Subject: [PATCH 07/17] fix all files (damn, we need an auto fixer...) --- .../src/rules/member-ordering.ts | 13 ++-- .../eslint-plugin/src/rules/no-mixed-enums.ts | 2 +- .../rules/no-redundant-type-constituents.ts | 6 +- .../src/rules/prefer-literal-enum-member.ts | 3 +- packages/eslint-plugin/tests/docs.test.ts | 10 +-- .../tests/rules/array-type.test.ts | 4 +- .../tests/rules/ban-types.test.ts | 2 +- .../tests/rules/comma-spacing.test.ts | 2 +- .../consistent-generic-constructors.test.ts | 25 ++++--- .../rules/consistent-type-definitions.test.ts | 16 ++-- .../tests/rules/indent/indent.test.ts | 2 +- .../no-confusing-void-expression.test.ts | 8 +- .../no-duplicate-type-constituents.test.ts | 32 ++++---- .../tests/rules/no-empty-interface.test.ts | 4 +- ...o-non-null-asserted-optional-chain.test.ts | 12 +-- .../rules/no-unnecessary-condition.test.ts | 2 +- .../no-unnecessary-type-constraint.test.ts | 18 ++--- .../eslint-plugin/tests/rules/quotes.test.ts | 2 +- .../eslint-plugin/tests/rules/semi.test.ts | 4 +- .../rules/sort-type-constituents.test.ts | 2 +- .../rules/strict-boolean-expressions.test.ts | 75 +++++++++++-------- packages/eslint-plugin/tests/schemas.test.ts | 2 +- 22 files changed, 131 insertions(+), 115 deletions(-) diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index 95d6c3715e0f..ccc7faf1156d 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -396,8 +396,8 @@ function getNodeType(node: Member): MemberKind | null { return node.value && functionExpressions.includes(node.value.type) ? 'method' : node.readonly - ? 'readonly-field' - : 'field'; + ? 'readonly-field' + : 'field'; case AST_NODE_TYPES.TSPropertySignature: return node.readonly ? 'readonly-field' : 'field'; case AST_NODE_TYPES.TSIndexSignature: @@ -555,8 +555,8 @@ function getRank( 'static' in node && node.static ? 'static' : abstract - ? 'abstract' - : 'instance'; + ? 'abstract' + : 'instance'; const accessibility = getAccessibility(node); // Collect all existing member groups that apply to this node... @@ -578,7 +578,7 @@ function getRank( if (type === 'readonly-field') { memberGroups.push(`${accessibility}-decorated-field`); - memberGroups.push(`decorated-field`); + memberGroups.push('decorated-field'); } } @@ -666,7 +666,8 @@ export default createRule({ 'Member {{member}} should be declared before member {{beforeMember}}.', incorrectGroupOrder: 'Member {{name}} should be declared before all {{rank}} definitions.', - incorrectRequiredMembersOrder: `Member {{member}} should be declared after all {{optionalOrRequired}} members.`, + incorrectRequiredMembersOrder: + 'Member {{member}} should be declared after all {{optionalOrRequired}} members.', }, schema: [ { diff --git a/packages/eslint-plugin/src/rules/no-mixed-enums.ts b/packages/eslint-plugin/src/rules/no-mixed-enums.ts index 2b79f31a67e6..1510076c30e3 100644 --- a/packages/eslint-plugin/src/rules/no-mixed-enums.ts +++ b/packages/eslint-plugin/src/rules/no-mixed-enums.ts @@ -23,7 +23,7 @@ export default createRule({ requiresTypeChecking: true, }, messages: { - mixed: `Mixing number and string enums can be confusing.`, + mixed: 'Mixing number and string enums can be confusing.', }, schema: [], type: 'problem', diff --git a/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts index a9b22e71de11..70b7caa12214 100644 --- a/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts @@ -197,8 +197,10 @@ export default createRule({ requiresTypeChecking: true, }, messages: { - literalOverridden: `{{literal}} is overridden by {{primitive}} in this union type.`, - primitiveOverridden: `{{primitive}} is overridden by the {{literal}} in this intersection type.`, + literalOverridden: + '{{literal}} is overridden by {{primitive}} in this union type.', + primitiveOverridden: + '{{primitive}} is overridden by the {{literal}} in this intersection type.', overridden: `'{{typeName}}' is overridden by other types in this {{container}} type.`, overrides: `'{{typeName}}' overrides all other types in this {{container}} type.`, }, diff --git a/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts b/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts index ba659d1c40ed..2bf82d01a9e5 100644 --- a/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts +++ b/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts @@ -12,7 +12,8 @@ export default createRule({ requiresTypeChecking: false, }, messages: { - notLiteral: `Explicit enum value must only be a literal value (string, number, boolean, etc).`, + notLiteral: + 'Explicit enum value must only be a literal value (string, number, boolean, etc).', }, schema: [ { diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index ce9f3115a02f..fd05d1096e58 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -82,16 +82,16 @@ describe('Validating rule docs', () => { test(`${ruleName}.md must next have a blockquote directing to website`, () => { expect(tokens[2]).toMatchObject({ text: [ - `🛑 This file is source code, not the primary documentation location! 🛑`, - ``, + '🛑 This file is source code, not the primary documentation location! 🛑', + '', `See **https://typescript-eslint.io/rules/${ruleName}** for documentation.`, - ``, + '', ].join('\n'), type: 'blockquote', }); }); - test(`headers must be title-cased`, () => { + test('headers must be title-cased', () => { // Get all H2 headers objects as the other levels are variable by design. const headers = tokens.filter(tokenIsH2); @@ -130,7 +130,7 @@ describe('Validating rule metadata', () => { } for (const [ruleName, rule] of rulesData) { - describe(`${ruleName}`, () => { + describe(ruleName, () => { it('`name` field in rule must match the filename', () => { // validate if rule name is same as url // there is no way to access this field but its used only in generation of docs url diff --git a/packages/eslint-plugin/tests/rules/array-type.test.ts b/packages/eslint-plugin/tests/rules/array-type.test.ts index 44be83ff63db..d9613c0f39f1 100644 --- a/packages/eslint-plugin/tests/rules/array-type.test.ts +++ b/packages/eslint-plugin/tests/rules/array-type.test.ts @@ -2162,14 +2162,14 @@ describe('schema validation', () => { // https://github.com/typescript-eslint/typescript-eslint/issues/6852 test("array-type does not accept 'simple-array' option", () => { if (areOptionsValid(rule, [{ default: 'simple-array' }])) { - throw new Error(`Options succeeded validation for bad options`); + throw new Error('Options succeeded validation for bad options'); } }); // https://github.com/typescript-eslint/typescript-eslint/issues/6892 test('array-type does not accept non object option', () => { if (areOptionsValid(rule, ['array'])) { - throw new Error(`Options succeeded validation for bad options`); + throw new Error('Options succeeded validation for bad options'); } }); }); diff --git a/packages/eslint-plugin/tests/rules/ban-types.test.ts b/packages/eslint-plugin/tests/rules/ban-types.test.ts index c74df23f4f32..48153d4637d6 100644 --- a/packages/eslint-plugin/tests/rules/ban-types.test.ts +++ b/packages/eslint-plugin/tests/rules/ban-types.test.ts @@ -504,7 +504,7 @@ let baz: object = {}; }, { code: noFormat`let a: Foo< F >;`, - output: `let a: Foo< T >;`, + output: 'let a: Foo< T >;', errors: [ { messageId: 'bannedTypeMessage', diff --git a/packages/eslint-plugin/tests/rules/comma-spacing.test.ts b/packages/eslint-plugin/tests/rules/comma-spacing.test.ts index d86edef2ae34..d7dc6177bfe5 100644 --- a/packages/eslint-plugin/tests/rules/comma-spacing.test.ts +++ b/packages/eslint-plugin/tests/rules/comma-spacing.test.ts @@ -439,7 +439,7 @@ ruleTester.run('comma-spacing', rule, { data: { loc: 'before' }, }, { - messageId: `missing`, + messageId: 'missing', column: 16, line: 1, data: { loc: 'after' }, 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 f21dac8f6399..2f68854acd1d 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -162,7 +162,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: `const a = new Map();`, + output: 'const a = new Map();', }, { code: noFormat`const a: Map< string, number > = new Map();`, @@ -171,7 +171,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: `const a = new Map< string, number >();`, + output: 'const a = new Map< string, number >();', }, { code: noFormat`const a: Map = new Map ();`, @@ -180,7 +180,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: `const a = new Map ();`, + output: 'const a = new Map ();', }, { code: noFormat`const a: Foo = new Foo;`, @@ -189,7 +189,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: `const a = new Foo();`, + output: 'const a = new Foo();', }, { code: 'const a: /* comment */ Foo/* another */ = new Foo();', @@ -198,7 +198,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: `const a = new Foo/* comment *//* another */();`, + output: 'const a = new Foo/* comment *//* another */();', }, { code: 'const a: Foo/* comment */ = new Foo /* another */();', @@ -207,7 +207,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: `const a = new Foo/* comment */ /* another */();`, + output: 'const a = new Foo/* comment */ /* another */();', }, { code: noFormat`const a: Foo = new \n Foo \n ();`, @@ -216,7 +216,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: `const a = new \n Foo \n ();`, + output: 'const a = new \n Foo \n ();', }, { code: ` @@ -349,7 +349,7 @@ const a = function (a = new Foo()) {}; messageId: 'preferTypeAnnotation', }, ], - output: `const a: Map = new Map ();`, + output: 'const a: Map = new Map ();', }, { code: noFormat`const a = new Map< string, number >();`, @@ -359,7 +359,7 @@ const a = function (a = new Foo()) {}; messageId: 'preferTypeAnnotation', }, ], - output: `const a: Map< string, number > = new Map();`, + output: 'const a: Map< string, number > = new Map();', }, { code: noFormat`const a = new \n Foo \n ();`, @@ -369,7 +369,7 @@ const a = function (a = new Foo()) {}; messageId: 'preferTypeAnnotation', }, ], - output: `const a: Foo = new \n Foo \n ();`, + output: 'const a: Foo = new \n Foo \n ();', }, { code: 'const a = new Foo/* comment */ /* another */();', @@ -379,7 +379,7 @@ const a = function (a = new Foo()) {}; messageId: 'preferTypeAnnotation', }, ], - output: `const a: Foo = new Foo/* comment */ /* another */();`, + output: 'const a: Foo = new Foo/* comment */ /* another */();', }, { code: 'const a = new Foo();', @@ -389,7 +389,8 @@ const a = function (a = new Foo()) {}; messageId: 'preferTypeAnnotation', }, ], - output: `const a: Foo = new Foo();`, + output: + 'const a: Foo = new Foo();', }, { code: ` diff --git a/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts b/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts index b0a2092994d3..41dd3f12e041 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts @@ -64,7 +64,7 @@ export type W = { invalid: [ { code: noFormat`type T = { x: number; };`, - output: `interface T { x: number; }`, + output: 'interface T { x: number; }', options: ['interface'], errors: [ { @@ -76,7 +76,7 @@ export type W = { }, { code: noFormat`type T={ x: number; };`, - output: `interface T { x: number; }`, + output: 'interface T { x: number; }', options: ['interface'], errors: [ { @@ -88,7 +88,7 @@ export type W = { }, { code: noFormat`type T= { x: number; };`, - output: `interface T { x: number; }`, + output: 'interface T { x: number; }', options: ['interface'], errors: [ { @@ -120,7 +120,7 @@ export interface W { }, { code: noFormat`interface T { x: number; }`, - output: `type T = { x: number; }`, + output: 'type T = { x: number; }', options: ['type'], errors: [ { @@ -132,7 +132,7 @@ export interface W { }, { code: noFormat`interface T{ x: number; }`, - output: `type T = { x: number; }`, + output: 'type T = { x: number; }', options: ['type'], errors: [ { @@ -144,7 +144,7 @@ export interface W { }, { code: noFormat`interface T { x: number; }`, - output: `type T = { x: number; }`, + output: 'type T = { x: number; }', options: ['type'], errors: [ { @@ -156,7 +156,7 @@ export interface W { }, { code: noFormat`interface A extends B, C { x: number; };`, - output: `type A = { x: number; } & B & C;`, + output: 'type A = { x: number; } & B & C;', options: ['type'], errors: [ { @@ -168,7 +168,7 @@ export interface W { }, { code: noFormat`interface A extends B, C { x: number; };`, - output: `type A = { x: number; } & B & C;`, + output: 'type A = { x: number; } & B & C;', options: ['type'], errors: [ { diff --git a/packages/eslint-plugin/tests/rules/indent/indent.test.ts b/packages/eslint-plugin/tests/rules/indent/indent.test.ts index 5974cb5198ee..49c4ce4cda30 100644 --- a/packages/eslint-plugin/tests/rules/indent/indent.test.ts +++ b/packages/eslint-plugin/tests/rules/indent/indent.test.ts @@ -20,7 +20,7 @@ type Options = InferOptionsTypeFromRule; * Marks a test case as a plain javascript case which should be indented the same */ function nonTsTestCase(example: TemplateStringsArray): string { - return [`// Non-TS Test Case`, example].join('\n'); + return ['// Non-TS Test Case', example].join('\n'); } const individualNodeTests = [ diff --git a/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts b/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts index c03d89ed6a61..c926ce6a9690 100644 --- a/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts +++ b/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts @@ -203,7 +203,7 @@ function notcool(input: string) { { code: '(foo: undefined) => foo && console.log(foo);', errors: [{ line: 1, column: 28, messageId: 'invalidVoidExprArrow' }], - output: `(foo: undefined) => { foo && console.log(foo); };`, + output: '(foo: undefined) => { foo && console.log(foo); };', }, { code: 'foo => foo || console.log(foo);', @@ -212,12 +212,12 @@ function notcool(input: string) { { code: '(foo: undefined) => foo || console.log(foo);', errors: [{ line: 1, column: 28, messageId: 'invalidVoidExprArrow' }], - output: `(foo: undefined) => { foo || console.log(foo); };`, + output: '(foo: undefined) => { foo || console.log(foo); };', }, { code: '(foo: void) => foo || console.log(foo);', errors: [{ line: 1, column: 23, messageId: 'invalidVoidExprArrow' }], - output: `(foo: void) => { foo || console.log(foo); };`, + output: '(foo: void) => { foo || console.log(foo); };', }, { code: 'foo => (foo ? console.log(true) : console.log(false));', @@ -225,7 +225,7 @@ function notcool(input: string) { { line: 1, column: 15, messageId: 'invalidVoidExprArrow' }, { line: 1, column: 35, messageId: 'invalidVoidExprArrow' }, ], - output: `foo => { foo ? console.log(true) : console.log(false); };`, + output: 'foo => { foo ? console.log(true) : console.log(false); };', }, { code: ` diff --git a/packages/eslint-plugin/tests/rules/no-duplicate-type-constituents.test.ts b/packages/eslint-plugin/tests/rules/no-duplicate-type-constituents.test.ts index 376e3fa22683..92076923e79c 100644 --- a/packages/eslint-plugin/tests/rules/no-duplicate-type-constituents.test.ts +++ b/packages/eslint-plugin/tests/rules/no-duplicate-type-constituents.test.ts @@ -154,7 +154,7 @@ type T = Record; invalid: [ { code: 'type T = 1 | 1;', - output: `type T = 1 ;`, + output: 'type T = 1 ;', errors: [ { messageId: 'duplicate', @@ -167,7 +167,7 @@ type T = Record; }, { code: 'type T = true & true;', - output: `type T = true ;`, + output: 'type T = true ;', errors: [ { messageId: 'duplicate', @@ -180,7 +180,7 @@ type T = Record; }, { code: 'type T = null | null;', - output: `type T = null ;`, + output: 'type T = null ;', errors: [ { messageId: 'duplicate', @@ -193,7 +193,7 @@ type T = Record; }, { code: 'type T = any | any;', - output: `type T = any ;`, + output: 'type T = any ;', errors: [ { messageId: 'duplicate', @@ -206,7 +206,7 @@ type T = Record; }, { code: 'type T = { a: string | string };', - output: `type T = { a: string };`, + output: 'type T = { a: string };', errors: [ { messageId: 'duplicate', @@ -219,7 +219,7 @@ type T = Record; }, { code: 'type T = { a: string } | { a: string };', - output: `type T = { a: string } ;`, + output: 'type T = { a: string } ;', errors: [ { messageId: 'duplicate', @@ -232,7 +232,7 @@ type T = Record; }, { code: 'type T = { a: string; b: number } | { a: string; b: number };', - output: `type T = { a: string; b: number } ;`, + output: 'type T = { a: string; b: number } ;', errors: [ { messageId: 'duplicate', @@ -245,7 +245,7 @@ type T = Record; }, { code: 'type T = Set | Set;', - output: `type T = Set ;`, + output: 'type T = Set ;', errors: [ { messageId: 'duplicate', @@ -277,7 +277,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = Class | Class;', - output: `type T = Class ;`, + output: 'type T = Class ;', errors: [ { messageId: 'duplicate', @@ -290,7 +290,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = string[] | string[];', - output: `type T = string[] ;`, + output: 'type T = string[] ;', errors: [ { messageId: 'duplicate', @@ -303,7 +303,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = string[][] | string[][];', - output: `type T = string[][] ;`, + output: 'type T = string[][] ;', errors: [ { messageId: 'duplicate', @@ -316,7 +316,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = [1, 2, 3] | [1, 2, 3];', - output: `type T = [1, 2, 3] ;`, + output: 'type T = [1, 2, 3] ;', errors: [ { messageId: 'duplicate', @@ -329,7 +329,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = () => string | string;', - output: `type T = () => string ;`, + output: 'type T = () => string ;', errors: [ { messageId: 'duplicate', @@ -342,7 +342,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = () => null | null;', - output: `type T = () => null ;`, + output: 'type T = () => null ;', errors: [ { messageId: 'duplicate', @@ -355,7 +355,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = (arg: string | string) => void;', - output: `type T = (arg: string ) => void;`, + output: 'type T = (arg: string ) => void;', errors: [ { messageId: 'duplicate', @@ -601,7 +601,7 @@ type T = A ; messageId: 'duplicate', data: { type: 'Union', - previous: `A`, + previous: 'A', }, }, { diff --git a/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts index 3711f874a3b3..48f159aaa2cf 100644 --- a/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts +++ b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts @@ -171,7 +171,7 @@ type Bar = Foo }, { code: 'interface Foo extends Array {}', - output: `type Foo = Array`, + output: 'type Foo = Array', errors: [ { messageId: 'noEmptyWithSuper', @@ -182,7 +182,7 @@ type Bar = Foo }, { code: 'interface Foo extends Array {}', - output: `type Foo = Array`, + output: 'type Foo = Array', errors: [ { messageId: 'noEmptyWithSuper', diff --git a/packages/eslint-plugin/tests/rules/no-non-null-asserted-optional-chain.test.ts b/packages/eslint-plugin/tests/rules/no-non-null-asserted-optional-chain.test.ts index a9fda0210fdf..e9308fddba6a 100644 --- a/packages/eslint-plugin/tests/rules/no-non-null-asserted-optional-chain.test.ts +++ b/packages/eslint-plugin/tests/rules/no-non-null-asserted-optional-chain.test.ts @@ -87,7 +87,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: `(foo?.bar).baz`, + output: '(foo?.bar).baz', }, ], }, @@ -101,7 +101,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: `(foo?.bar)().baz`, + output: '(foo?.bar)().baz', }, ], }, @@ -115,7 +115,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: `(foo?.bar)`, + output: '(foo?.bar)', }, ], }, @@ -129,7 +129,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: `(foo?.bar)()`, + output: '(foo?.bar)()', }, ], }, @@ -143,7 +143,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: `(foo?.bar)`, + output: '(foo?.bar)', }, ], }, @@ -157,7 +157,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: `(foo?.bar)()`, + output: '(foo?.bar)()', }, ], }, diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts index 8c891aea6248..4934f6413c70 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -812,7 +812,7 @@ const t1 = b2 && b1 ? 'yes' : 'no'; unnecessaryConditionTest('object | true', 'alwaysTruthy'), unnecessaryConditionTest('"" | false', 'alwaysFalsy'), // Two falsy literals unnecessaryConditionTest('"always truthy"', 'alwaysTruthy'), - unnecessaryConditionTest(`undefined`, 'alwaysFalsy'), + unnecessaryConditionTest('undefined', 'alwaysFalsy'), unnecessaryConditionTest('null', 'alwaysFalsy'), unnecessaryConditionTest('void', 'alwaysFalsy'), unnecessaryConditionTest('never', 'never'), diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts index 2b1f6841f2c1..9ec31dce3e44 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts @@ -117,7 +117,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: `const data = () => {};`, + output: 'const data = () => {};', }, ], }, @@ -141,7 +141,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: `const data = () => {};`, + output: 'const data = () => {};', }, ], }, @@ -161,7 +161,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: `const data = () => {};`, + output: 'const data = () => {};', }, ], }, @@ -181,7 +181,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: `const data = () => {};`, + output: 'const data = () => {};', }, ], }, @@ -205,7 +205,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: `const data = () => {};`, + output: 'const data = () => {};', }, ], }, @@ -229,7 +229,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: `const data = () => {};`, + output: 'const data = () => {};', }, ], }, @@ -253,7 +253,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: `const data = () => {};`, + output: 'const data = () => {};', }, ], }, @@ -301,7 +301,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: `const data = () => {};`, + output: 'const data = () => {};', }, ], }, @@ -315,7 +315,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: `const data = () => {};`, + output: 'const data = () => {};', }, ], }, diff --git a/packages/eslint-plugin/tests/rules/quotes.test.ts b/packages/eslint-plugin/tests/rules/quotes.test.ts index d6c25e6f41ae..1c718dab212a 100644 --- a/packages/eslint-plugin/tests/rules/quotes.test.ts +++ b/packages/eslint-plugin/tests/rules/quotes.test.ts @@ -688,7 +688,7 @@ abstract class Foo { { code: '
', output: "
", - options: [`single`], + options: ['single'], parserOptions: { ecmaFeatures: { jsx: true, diff --git a/packages/eslint-plugin/tests/rules/semi.test.ts b/packages/eslint-plugin/tests/rules/semi.test.ts index e1f53043a9ce..4db70d358b64 100644 --- a/packages/eslint-plugin/tests/rules/semi.test.ts +++ b/packages/eslint-plugin/tests/rules/semi.test.ts @@ -229,7 +229,7 @@ class PanCamera extends FreeCamera { `, // https://github.com/typescript-eslint/typescript-eslint/issues/123 'export default interface test {}', - `declare function declareFn(): string;`, + 'declare function declareFn(): string;', // ESLint 'var x = 5;', 'var x =5, y;', @@ -739,7 +739,7 @@ class PanCamera extends FreeCamera { ...[ { - code: `declare function declareFn(): string;`, + code: 'declare function declareFn(): string;', errors: [ { line: 1, diff --git a/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts b/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts index e1a2afc38288..6af33968aab1 100644 --- a/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts +++ b/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts @@ -366,7 +366,7 @@ type T = 1 | string | {} | A; ...invalid('&'), { code: 'type T = (B | C) & A;', - output: `type T = A & (B | C);`, + output: 'type T = A & (B | C);', errors: [ { messageId: 'notSortedNamed', diff --git a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts index 9a285b70d186..37b6bb0de44f 100644 --- a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts @@ -701,7 +701,7 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareStringLength', - output: ` declare const x: string; if (x.length > 0) {}`, + output: ' declare const x: string; if (x.length > 0) {}', }, { messageId: 'conditionFixCompareEmptyString', @@ -709,7 +709,7 @@ if (y) { }, { messageId: 'conditionFixCastBoolean', - output: ` declare const x: string; if (Boolean(x)) {}`, + output: ' declare const x: string; if (Boolean(x)) {}', }, ], }, @@ -720,7 +720,7 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareStringLength', - output: ` (x: string) => (x.length === 0);`, + output: ' (x: string) => (x.length === 0);', }, { messageId: 'conditionFixCompareEmptyString', @@ -728,7 +728,7 @@ if (y) { }, { messageId: 'conditionFixCastBoolean', - output: ` (x: string) => (!Boolean(x));`, + output: ' (x: string) => (!Boolean(x));', }, ], }, @@ -739,7 +739,8 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareStringLength', - output: ` (x: T) => (x.length > 0) ? 1 : 0;`, + output: + ' (x: T) => (x.length > 0) ? 1 : 0;', }, { messageId: 'conditionFixCompareEmptyString', @@ -747,7 +748,8 @@ if (y) { }, { messageId: 'conditionFixCastBoolean', - output: ` (x: T) => (Boolean(x)) ? 1 : 0;`, + output: + ' (x: T) => (Boolean(x)) ? 1 : 0;', }, ], }, @@ -775,16 +777,16 @@ if (y) { { messageId: 'conditionFixCompareZero', // TODO: fix compare zero suggestion for bigint - output: `while (0n !== 0) {}`, + output: 'while (0n !== 0) {}', }, { // TODO: remove check NaN suggestion for bigint messageId: 'conditionFixCompareNaN', - output: `while (!Number.isNaN(0n)) {}`, + output: 'while (!Number.isNaN(0n)) {}', }, { messageId: 'conditionFixCastBoolean', - output: `while (Boolean(0n)) {}`, + output: 'while (Boolean(0n)) {}', }, ], }, @@ -795,15 +797,15 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareZero', - output: ` for (; 123 !== 0;) {}`, + output: ' for (; 123 !== 0;) {}', }, { messageId: 'conditionFixCompareNaN', - output: ` for (; !Number.isNaN(123);) {}`, + output: ' for (; !Number.isNaN(123);) {}', }, { messageId: 'conditionFixCastBoolean', - output: ` for (; Boolean(123);) {}`, + output: ' for (; Boolean(123);) {}', }, ], }, @@ -814,15 +816,16 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareZero', - output: ` declare const x: number; if (x !== 0) {}`, + output: ' declare const x: number; if (x !== 0) {}', }, { messageId: 'conditionFixCompareNaN', - output: ` declare const x: number; if (!Number.isNaN(x)) {}`, + output: + ' declare const x: number; if (!Number.isNaN(x)) {}', }, { messageId: 'conditionFixCastBoolean', - output: ` declare const x: number; if (Boolean(x)) {}`, + output: ' declare const x: number; if (Boolean(x)) {}', }, ], }, @@ -834,16 +837,16 @@ if (y) { { messageId: 'conditionFixCompareZero', // TODO: fix compare zero suggestion for bigint - output: ` (x: bigint) => x === 0;`, + output: ' (x: bigint) => x === 0;', }, { // TODO: remove check NaN suggestion for bigint messageId: 'conditionFixCompareNaN', - output: ` (x: bigint) => Number.isNaN(x);`, + output: ' (x: bigint) => Number.isNaN(x);', }, { messageId: 'conditionFixCastBoolean', - output: ` (x: bigint) => !Boolean(x);`, + output: ' (x: bigint) => !Boolean(x);', }, ], }, @@ -854,15 +857,17 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareZero', - output: ` (x: T) => (x !== 0) ? 1 : 0;`, + output: ' (x: T) => (x !== 0) ? 1 : 0;', }, { messageId: 'conditionFixCompareNaN', - output: ` (x: T) => (!Number.isNaN(x)) ? 1 : 0;`, + output: + ' (x: T) => (!Number.isNaN(x)) ? 1 : 0;', }, { messageId: 'conditionFixCastBoolean', - output: ` (x: T) => (Boolean(x)) ? 1 : 0;`, + output: + ' (x: T) => (Boolean(x)) ? 1 : 0;', }, ], }, @@ -892,15 +897,18 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareZero', - output: ` declare const a: any[] & { notLength: number }; if (a.notLength !== 0) {}`, + output: + ' declare const a: any[] & { notLength: number }; if (a.notLength !== 0) {}', }, { messageId: 'conditionFixCompareNaN', - output: ` declare const a: any[] & { notLength: number }; if (!Number.isNaN(a.notLength)) {}`, + output: + ' declare const a: any[] & { notLength: number }; if (!Number.isNaN(a.notLength)) {}', }, { messageId: 'conditionFixCastBoolean', - output: ` declare const a: any[] & { notLength: number }; if (Boolean(a.notLength)) {}`, + output: + ' declare const a: any[] & { notLength: number }; if (Boolean(a.notLength)) {}', }, ], }, @@ -958,11 +966,11 @@ if (y) { suggestions: [ { messageId: 'conditionFixDefaultFalse', - output: `declare const x: boolean | null; if (x ?? false) {}`, + output: 'declare const x: boolean | null; if (x ?? false) {}', }, { messageId: 'conditionFixCompareTrue', - output: `declare const x: boolean | null; if (x === true) {}`, + output: 'declare const x: boolean | null; if (x === true) {}', }, ], }, @@ -973,11 +981,11 @@ if (y) { suggestions: [ { messageId: 'conditionFixDefaultFalse', - output: ` (x?: boolean) => !(x ?? false);`, + output: ' (x?: boolean) => !(x ?? false);', }, { messageId: 'conditionFixCompareFalse', - output: ` (x?: boolean) => x === false;`, + output: ' (x?: boolean) => x === false;', }, ], }, @@ -988,11 +996,13 @@ if (y) { suggestions: [ { messageId: 'conditionFixDefaultFalse', - output: ` (x: T) => (x ?? false) ? 1 : 0;`, + output: + ' (x: T) => (x ?? false) ? 1 : 0;', }, { messageId: 'conditionFixCompareTrue', - output: ` (x: T) => (x === true) ? 1 : 0;`, + output: + ' (x: T) => (x === true) ? 1 : 0;', }, ], }, @@ -1026,7 +1036,7 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareNullish', - output: ` (x?: { a: number }) => x == null;`, + output: ' (x?: { a: number }) => x == null;', }, ], }, @@ -1037,7 +1047,8 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareNullish', - output: ` (x: T) => (x != null) ? 1 : 0;`, + output: + ' (x: T) => (x != null) ? 1 : 0;', }, ], }, diff --git a/packages/eslint-plugin/tests/schemas.test.ts b/packages/eslint-plugin/tests/schemas.test.ts index 5524203d1815..4da86cdab81a 100644 --- a/packages/eslint-plugin/tests/schemas.test.ts +++ b/packages/eslint-plugin/tests/schemas.test.ts @@ -180,7 +180,7 @@ describe('Rule schemas should validate options correctly', () => { test(`${ruleName} rejects arbitrary options`, () => { if (areOptionsValid(rule, [{ 'arbitrary-schemas.test.ts': true }])) { - throw new Error(`Options succeeded validation for arbitrary options`); + throw new Error('Options succeeded validation for arbitrary options'); } }); } From fbdd6f34521611330e4ee203d68284e9bdfda437 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Sun, 10 Dec 2023 09:56:08 +0200 Subject: [PATCH 08/17] wip --- .../rules/no-useless-template-literals.md | 8 +- .../src/rules/member-ordering.ts | 8 +- .../src/rules/no-useless-template-literals.ts | 34 ++++---- .../no-useless-template-literals.test.ts | 81 ++++++++++++++++--- 4 files changed, 95 insertions(+), 36 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-useless-template-literals.md b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md index 4b2dae0d2879..94ebd43d704d 100644 --- a/packages/eslint-plugin/docs/rules/no-useless-template-literals.md +++ b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md @@ -22,8 +22,8 @@ const stringWithNumber = `1 + 1 = ${2}`; const stringWithBoolean = `${'true is '}${true}`; -const string = 'a'; -const wrappedString = `${string}`; +const text = 'a'; +const wrappedString = `${text}`; declare const intersectionWithString: string & { _brand: 'test-brand' }; const wrappedIntersection = `${intersectionWithString}`; @@ -39,8 +39,8 @@ const stringWithNumber = '1 + 1 = 2'; const stringWithBoolean = 'true is true'; -const string = 'a'; -const wrappedString = string; +const text = 'a'; +const wrappedString = text; declare const intersectionWithString: string & { _brand: 'test-brand' }; const wrappedIntersection = intersectionWithString; diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index ccc7faf1156d..b7310d7a2540 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -396,8 +396,8 @@ function getNodeType(node: Member): MemberKind | null { return node.value && functionExpressions.includes(node.value.type) ? 'method' : node.readonly - ? 'readonly-field' - : 'field'; + ? 'readonly-field' + : 'field'; case AST_NODE_TYPES.TSPropertySignature: return node.readonly ? 'readonly-field' : 'field'; case AST_NODE_TYPES.TSIndexSignature: @@ -555,8 +555,8 @@ function getRank( 'static' in node && node.static ? 'static' : abstract - ? 'abstract' - : 'instance'; + ? 'abstract' + : 'instance'; const accessibility = getAccessibility(node); // Collect all existing member groups that apply to this node... diff --git a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts index c4e51e6091ab..9034a0ff18af 100644 --- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -17,7 +17,7 @@ export default createRule<[], MessageId>({ type: 'problem', docs: { description: 'Disallow unnecessary template literals', - recommended: 'recommended', + recommended: 'strict', requiresTypeChecking: true, }, messages: { @@ -30,7 +30,9 @@ export default createRule<[], MessageId>({ create(context) { const services = getParserServices(context); - function isUnderlyingTypeString(expression: TSESTree.Expression): boolean { + function isUnderlyingTypeString( + expression: TSESTree.Expression, + ): expression is TSESTree.StringLiteral | TSESTree.Identifier { const type = getConstrainedTypeAtLocation(services, expression); const isString = (t: ts.Type): boolean => { @@ -59,7 +61,6 @@ export default createRule<[], MessageId>({ node.quasis[0].value.raw === '' && node.quasis[1].value.raw === '' && node.expressions.length === 1 && - node.expressions[0].type === AST_NODE_TYPES.Identifier && isUnderlyingTypeString(node.expressions[0]); if (hasSingleStringVariable) { @@ -71,26 +72,21 @@ export default createRule<[], MessageId>({ return; } - const allowedChars = ['\r', '\n', "'", '"']; + const stringLiteralExpressions = node.expressions.filter( + (expression): expression is TSESTree.StringLiteral => { + return ( + isUnderlyingTypeString(expression) && + expression.type === AST_NODE_TYPES.Literal + ); + }, + ); - const hasStringWithAllowedChars = node.quasis.some(quasi => { - return new RegExp(`[${allowedChars.join('')}]`).test(quasi.value.raw); - }); - - if (hasStringWithAllowedChars) { - return; - } - - const allAreLiterals = node.expressions.every(expression => { - return expression.type === AST_NODE_TYPES.Literal; - }); - - if (allAreLiterals) { + stringLiteralExpressions.forEach(stringLiteral => { context.report({ - node, + node: stringLiteral, messageId: 'noUselessTemplateLiteral', }); - } + }); }, }; }, diff --git a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts index f19fae12c835..5cc24bf8b7d7 100644 --- a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts +++ b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts @@ -17,6 +17,8 @@ ruleTester.run('no-useless-template-literals', rule, { valid: [ "const string = 'a';", + 'const string = `a`;', + ` const string = 'a'; const concatenated = \`\${string}b\`; @@ -68,6 +70,38 @@ ruleTester.run('no-useless-template-literals', rule, { tag\`\${'a'}\${'b'}\`; `, + ` + const wrappedNumber = \`\${1}\`; + `, + + ` + const wrappedBigint = \`\${1n}\`; + `, + + ` + const wrappedBoolean = \`\${true}\`; + `, + + ` + const wrappedNull = \`\${null}\`; + `, + + ` + const wrappedUndefined = \`\${undefined}\`; + `, + + ` + const wrappedFunction = \`\${function(){}}\`; + `, + + ` + const wrappedArrowFunction = \`\${() => {}}\`; + `, + + ` + const wrappedFunctionWithArgs = \`\${(...args: any[]) => args}\`; + `, + ` const number = 1; const wrapped = \`\${number}\`; @@ -137,46 +171,57 @@ ruleTester.run('no-useless-template-literals', rule, { { messageId: 'noUselessTemplateLiteral', line: 2, - column: 30, + column: 33, + endColumn: 36, + }, + { + messageId: 'noUselessTemplateLiteral', + line: 2, + column: 39, + endColumn: 42, }, ], }, { code: ` - const concatenated = \`a\${'b'}\`; + const b = 'b'; + const concatenated = \`a\${b}\${'c'}\`; `, errors: [ { messageId: 'noUselessTemplateLiteral', - line: 2, - column: 30, + line: 3, + column: 38, + endColumn: 41, }, ], }, { code: ` - const concatenated = \`\${'1 + 1 = '}\${2}\`; + const concatenated = \`a\${'b'}\`; `, errors: [ { messageId: 'noUselessTemplateLiteral', line: 2, - column: 30, + column: 34, + endColumn: 37, }, ], }, { code: ` - const concatenated = \`1 + 1 = \${2}\`; + const concatenated = \`\${'1 + 1 = '}\${2}\`; `, errors: [ { messageId: 'noUselessTemplateLiteral', line: 2, - column: 30, + column: 33, + endColumn: 43, }, ], }, @@ -189,7 +234,8 @@ ruleTester.run('no-useless-template-literals', rule, { { messageId: 'noUselessTemplateLiteral', line: 2, - column: 30, + column: 33, + endColumn: 36, }, ], }, @@ -204,6 +250,21 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', line: 3, column: 25, + endColumn: 36, + }, + ], + }, + + { + code: ` + const wrappedSymbol = \`\${String(Symbol.for('test'))}\`; + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 2, + column: 31, + endColumn: 62, }, ], }, @@ -218,6 +279,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', line: 3, column: 25, + endColumn: 42, }, ], }, @@ -233,6 +295,7 @@ ruleTester.run('no-useless-template-literals', rule, { messageId: 'noUselessTemplateLiteral', line: 3, column: 27, + endColumn: 35, }, ], }, From 7f0cc392712c90c3c3b98cbcc88bf85aa6235499 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Sun, 10 Dec 2023 10:07:50 +0200 Subject: [PATCH 09/17] report on specific node --- .../src/rules/no-useless-template-literals.ts | 2 +- .../rules/no-useless-template-literals.test.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts index 9034a0ff18af..a4843ed17d77 100644 --- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -65,7 +65,7 @@ export default createRule<[], MessageId>({ if (hasSingleStringVariable) { context.report({ - node, + node: node.expressions[0], messageId: 'noUselessTemplateLiteral', }); diff --git a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts index 5cc24bf8b7d7..29ac1b397b7f 100644 --- a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts +++ b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts @@ -249,8 +249,8 @@ ruleTester.run('no-useless-template-literals', rule, { { messageId: 'noUselessTemplateLiteral', line: 3, - column: 25, - endColumn: 36, + column: 28, + endColumn: 34, }, ], }, @@ -263,8 +263,8 @@ ruleTester.run('no-useless-template-literals', rule, { { messageId: 'noUselessTemplateLiteral', line: 2, - column: 31, - endColumn: 62, + column: 34, + endColumn: 60, }, ], }, @@ -278,8 +278,8 @@ ruleTester.run('no-useless-template-literals', rule, { { messageId: 'noUselessTemplateLiteral', line: 3, - column: 25, - endColumn: 42, + column: 28, + endColumn: 40, }, ], }, @@ -294,8 +294,8 @@ ruleTester.run('no-useless-template-literals', rule, { { messageId: 'noUselessTemplateLiteral', line: 3, - column: 27, - endColumn: 35, + column: 30, + endColumn: 33, }, ], }, From d22b0f831c8c6ac549079591561b8eeae7088098 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Sun, 10 Dec 2023 10:09:25 +0200 Subject: [PATCH 10/17] fix docs --- .../eslint-plugin/docs/rules/no-useless-template-literals.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-useless-template-literals.md b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md index 94ebd43d704d..6038338e78fc 100644 --- a/packages/eslint-plugin/docs/rules/no-useless-template-literals.md +++ b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md @@ -23,7 +23,7 @@ const stringWithNumber = `1 + 1 = ${2}`; const stringWithBoolean = `${'true is '}${true}`; const text = 'a'; -const wrappedString = `${text}`; +const wrappedText = `${text}`; declare const intersectionWithString: string & { _brand: 'test-brand' }; const wrappedIntersection = `${intersectionWithString}`; @@ -40,7 +40,7 @@ const stringWithNumber = '1 + 1 = 2'; const stringWithBoolean = 'true is true'; const text = 'a'; -const wrappedString = text; +const wrappedText = text; declare const intersectionWithString: string & { _brand: 'test-brand' }; const wrappedIntersection = intersectionWithString; From 775594da12c9e5671209d529e1f5b8fa07b8f41b Mon Sep 17 00:00:00 2001 From: StyleShit Date: Sun, 10 Dec 2023 10:15:57 +0200 Subject: [PATCH 11/17] fix lint --- packages/eslint-plugin/src/rules/consistent-type-exports.ts | 2 +- packages/eslint-plugin/tests/rules/no-extra-parens.test.ts | 6 +++--- .../tests/rules/no-unnecessary-condition.test.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-type-exports.ts b/packages/eslint-plugin/src/rules/consistent-type-exports.ts index 127dde8831df..78efc59bb853 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-exports.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-exports.ts @@ -189,7 +189,7 @@ export default createRule({ // We have both type and value violations. const allExportNames = report.typeBasedSpecifiers.map( - specifier => `${specifier.local.name}`, + specifier => specifier.local.name, ); if (allExportNames.length === 1) { diff --git a/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts b/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts index 43f6902a20e3..ef335b488432 100644 --- a/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts +++ b/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts @@ -3,7 +3,7 @@ /* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ /* eslint-enable eslint-comments/no-use */ -import { RuleTester } from '@typescript-eslint/rule-tester'; +import { RuleTester, noFormat } from '@typescript-eslint/rule-tester'; import rule from '../../src/rules/no-extra-parens'; @@ -731,7 +731,7 @@ f<(number) | string>(1) }, { - code: ` + code: noFormat` const Component = (

@@ -743,7 +743,7 @@ const Component = ( /> ) `, - output: ` + output: noFormat` const Component =${' '}

diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts index 4934f6413c70..248bef016ff0 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -1235,7 +1235,7 @@ foo ?. foo ?. (); `, - output: ` + output: noFormat` let foo = () => {}; foo(); foo (); @@ -1285,7 +1285,7 @@ foo ?. foo ?. (bar); `, - output: ` + output: noFormat` let foo = () => {}; foo(bar); foo (bar); From 4da820d1af00c2c0dc941fed5ac973449aee8b31 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Sun, 10 Dec 2023 10:40:32 +0200 Subject: [PATCH 12/17] wip --- .../docs/rules/no-useless-template-literals.md | 10 +++++++--- .../src/configs/recommended-type-checked.ts | 1 - .../eslint-plugin/tests/rules/no-extra-parens.test.ts | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-useless-template-literals.md b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md index 6038338e78fc..9a5c780394e6 100644 --- a/packages/eslint-plugin/docs/rules/no-useless-template-literals.md +++ b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md @@ -18,7 +18,7 @@ This rule reports template literals that can be simplified to a normal string li const ab1 = `${'a'}${'b'}`; const ab2 = `a${'b'}`; -const stringWithNumber = `1 + 1 = ${2}`; +const stringWithNumber = `${'1 + 1 = '}${2}`; const stringWithBoolean = `${'true is '}${true}`; @@ -35,9 +35,9 @@ const wrappedIntersection = `${intersectionWithString}`; const ab1 = 'ab'; const ab2 = 'ab'; -const stringWithNumber = '1 + 1 = 2'; +const stringWithNumber = `1 + 1 = ${2}`; -const stringWithBoolean = 'true is true'; +const stringWithBoolean = `true is ${true}`; const text = 'a'; const wrappedText = text; @@ -48,6 +48,10 @@ const wrappedIntersection = intersectionWithString; +## When Not To Use It + +When you want to allow string expressions inside template literals. + ## Related To - [`restrict-template-expressions`](./restrict-template-expressions.md) diff --git a/packages/eslint-plugin/src/configs/recommended-type-checked.ts b/packages/eslint-plugin/src/configs/recommended-type-checked.ts index 46ac6ee4b3bd..ab0f50394612 100644 --- a/packages/eslint-plugin/src/configs/recommended-type-checked.ts +++ b/packages/eslint-plugin/src/configs/recommended-type-checked.ts @@ -41,7 +41,6 @@ export = { '@typescript-eslint/no-unsafe-return': 'error', 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': 'error', - '@typescript-eslint/no-useless-template-literals': 'error', '@typescript-eslint/no-var-requires': 'error', '@typescript-eslint/prefer-as-const': 'error', 'require-await': 'off', diff --git a/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts b/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts index ef335b488432..714dce655ba8 100644 --- a/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts +++ b/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts @@ -3,7 +3,7 @@ /* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ /* eslint-enable eslint-comments/no-use */ -import { RuleTester, noFormat } from '@typescript-eslint/rule-tester'; +import { noFormat, RuleTester } from '@typescript-eslint/rule-tester'; import rule from '../../src/rules/no-extra-parens'; From 1a545b8c3fc8ea97d8be866b56ad344c0cdd6d25 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Sun, 10 Dec 2023 11:12:21 +0200 Subject: [PATCH 13/17] fix lint --- .../tests/rules/no-useless-template-literals.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts index 29ac1b397b7f..8cbd16179965 100644 --- a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts +++ b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts @@ -91,7 +91,7 @@ ruleTester.run('no-useless-template-literals', rule, { `, ` - const wrappedFunction = \`\${function(){}}\`; + const wrappedFunction = \`\${function () {}}\`; `, ` From ba1ddbd64709289acb58646e11e9a59775936d36 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Tue, 12 Dec 2023 08:49:30 +0200 Subject: [PATCH 14/17] revert unrelated changes --- .../src/rules/member-ordering.ts | 5 +- .../eslint-plugin/src/rules/no-mixed-enums.ts | 2 +- .../rules/no-redundant-type-constituents.ts | 6 +- .../src/rules/prefer-literal-enum-member.ts | 3 +- packages/eslint-plugin/tests/docs.test.ts | 6 +- .../tests/rules/array-type.test.ts | 4 +- .../tests/rules/ban-types.test.ts | 2 +- .../tests/rules/comma-spacing.test.ts | 2 +- .../consistent-generic-constructors.test.ts | 25 +++---- .../rules/consistent-type-definitions.test.ts | 16 ++-- .../tests/rules/indent/indent.test.ts | 2 +- .../no-confusing-void-expression.test.ts | 8 +- .../no-duplicate-type-constituents.test.ts | 32 ++++---- .../tests/rules/no-empty-interface.test.ts | 4 +- ...o-non-null-asserted-optional-chain.test.ts | 12 +-- .../rules/no-unnecessary-condition.test.ts | 2 +- .../no-unnecessary-type-constraint.test.ts | 18 ++--- .../eslint-plugin/tests/rules/quotes.test.ts | 2 +- .../eslint-plugin/tests/rules/semi.test.ts | 4 +- .../rules/sort-type-constituents.test.ts | 2 +- .../rules/strict-boolean-expressions.test.ts | 75 ++++++++----------- packages/eslint-plugin/tests/schemas.test.ts | 2 +- 22 files changed, 109 insertions(+), 125 deletions(-) diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index b7310d7a2540..95d6c3715e0f 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -578,7 +578,7 @@ function getRank( if (type === 'readonly-field') { memberGroups.push(`${accessibility}-decorated-field`); - memberGroups.push('decorated-field'); + memberGroups.push(`decorated-field`); } } @@ -666,8 +666,7 @@ export default createRule({ 'Member {{member}} should be declared before member {{beforeMember}}.', incorrectGroupOrder: 'Member {{name}} should be declared before all {{rank}} definitions.', - incorrectRequiredMembersOrder: - 'Member {{member}} should be declared after all {{optionalOrRequired}} members.', + incorrectRequiredMembersOrder: `Member {{member}} should be declared after all {{optionalOrRequired}} members.`, }, schema: [ { diff --git a/packages/eslint-plugin/src/rules/no-mixed-enums.ts b/packages/eslint-plugin/src/rules/no-mixed-enums.ts index 1510076c30e3..2b79f31a67e6 100644 --- a/packages/eslint-plugin/src/rules/no-mixed-enums.ts +++ b/packages/eslint-plugin/src/rules/no-mixed-enums.ts @@ -23,7 +23,7 @@ export default createRule({ requiresTypeChecking: true, }, messages: { - mixed: 'Mixing number and string enums can be confusing.', + mixed: `Mixing number and string enums can be confusing.`, }, schema: [], type: 'problem', diff --git a/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts index 70b7caa12214..a9b22e71de11 100644 --- a/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts @@ -197,10 +197,8 @@ export default createRule({ requiresTypeChecking: true, }, messages: { - literalOverridden: - '{{literal}} is overridden by {{primitive}} in this union type.', - primitiveOverridden: - '{{primitive}} is overridden by the {{literal}} in this intersection type.', + literalOverridden: `{{literal}} is overridden by {{primitive}} in this union type.`, + primitiveOverridden: `{{primitive}} is overridden by the {{literal}} in this intersection type.`, overridden: `'{{typeName}}' is overridden by other types in this {{container}} type.`, overrides: `'{{typeName}}' overrides all other types in this {{container}} type.`, }, diff --git a/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts b/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts index 2bf82d01a9e5..ba659d1c40ed 100644 --- a/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts +++ b/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts @@ -12,8 +12,7 @@ export default createRule({ requiresTypeChecking: false, }, messages: { - notLiteral: - 'Explicit enum value must only be a literal value (string, number, boolean, etc).', + notLiteral: `Explicit enum value must only be a literal value (string, number, boolean, etc).`, }, schema: [ { diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index 1ed06ce0a8a0..8e0739aed38d 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -89,10 +89,10 @@ describe('Validating rule docs', () => { test(`${ruleName}.md must next have a blockquote directing to website`, () => { expect(tokens[2]).toMatchObject({ text: [ - '🛑 This file is source code, not the primary documentation location! 🛑', - '', + `🛑 This file is source code, not the primary documentation location! 🛑`, + ``, `See **https://typescript-eslint.io/rules/${ruleName}** for documentation.`, - '', + ``, ].join('\n'), type: 'blockquote', }); diff --git a/packages/eslint-plugin/tests/rules/array-type.test.ts b/packages/eslint-plugin/tests/rules/array-type.test.ts index d9613c0f39f1..44be83ff63db 100644 --- a/packages/eslint-plugin/tests/rules/array-type.test.ts +++ b/packages/eslint-plugin/tests/rules/array-type.test.ts @@ -2162,14 +2162,14 @@ describe('schema validation', () => { // https://github.com/typescript-eslint/typescript-eslint/issues/6852 test("array-type does not accept 'simple-array' option", () => { if (areOptionsValid(rule, [{ default: 'simple-array' }])) { - throw new Error('Options succeeded validation for bad options'); + throw new Error(`Options succeeded validation for bad options`); } }); // https://github.com/typescript-eslint/typescript-eslint/issues/6892 test('array-type does not accept non object option', () => { if (areOptionsValid(rule, ['array'])) { - throw new Error('Options succeeded validation for bad options'); + throw new Error(`Options succeeded validation for bad options`); } }); }); diff --git a/packages/eslint-plugin/tests/rules/ban-types.test.ts b/packages/eslint-plugin/tests/rules/ban-types.test.ts index 48153d4637d6..c74df23f4f32 100644 --- a/packages/eslint-plugin/tests/rules/ban-types.test.ts +++ b/packages/eslint-plugin/tests/rules/ban-types.test.ts @@ -504,7 +504,7 @@ let baz: object = {}; }, { code: noFormat`let a: Foo< F >;`, - output: 'let a: Foo< T >;', + output: `let a: Foo< T >;`, errors: [ { messageId: 'bannedTypeMessage', diff --git a/packages/eslint-plugin/tests/rules/comma-spacing.test.ts b/packages/eslint-plugin/tests/rules/comma-spacing.test.ts index d7dc6177bfe5..d86edef2ae34 100644 --- a/packages/eslint-plugin/tests/rules/comma-spacing.test.ts +++ b/packages/eslint-plugin/tests/rules/comma-spacing.test.ts @@ -439,7 +439,7 @@ ruleTester.run('comma-spacing', rule, { data: { loc: 'before' }, }, { - messageId: 'missing', + messageId: `missing`, column: 16, line: 1, data: { loc: 'after' }, 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 2f68854acd1d..f21dac8f6399 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -162,7 +162,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: 'const a = new Map();', + output: `const a = new Map();`, }, { code: noFormat`const a: Map< string, number > = new Map();`, @@ -171,7 +171,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: 'const a = new Map< string, number >();', + output: `const a = new Map< string, number >();`, }, { code: noFormat`const a: Map = new Map ();`, @@ -180,7 +180,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: 'const a = new Map ();', + output: `const a = new Map ();`, }, { code: noFormat`const a: Foo = new Foo;`, @@ -189,7 +189,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: 'const a = new Foo();', + output: `const a = new Foo();`, }, { code: 'const a: /* comment */ Foo/* another */ = new Foo();', @@ -198,7 +198,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: 'const a = new Foo/* comment *//* another */();', + output: `const a = new Foo/* comment *//* another */();`, }, { code: 'const a: Foo/* comment */ = new Foo /* another */();', @@ -207,7 +207,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: 'const a = new Foo/* comment */ /* another */();', + output: `const a = new Foo/* comment */ /* another */();`, }, { code: noFormat`const a: Foo = new \n Foo \n ();`, @@ -216,7 +216,7 @@ function a([a = new Foo()]) {} messageId: 'preferConstructor', }, ], - output: 'const a = new \n Foo \n ();', + output: `const a = new \n Foo \n ();`, }, { code: ` @@ -349,7 +349,7 @@ const a = function (a = new Foo()) {}; messageId: 'preferTypeAnnotation', }, ], - output: 'const a: Map = new Map ();', + output: `const a: Map = new Map ();`, }, { code: noFormat`const a = new Map< string, number >();`, @@ -359,7 +359,7 @@ const a = function (a = new Foo()) {}; messageId: 'preferTypeAnnotation', }, ], - output: 'const a: Map< string, number > = new Map();', + output: `const a: Map< string, number > = new Map();`, }, { code: noFormat`const a = new \n Foo \n ();`, @@ -369,7 +369,7 @@ const a = function (a = new Foo()) {}; messageId: 'preferTypeAnnotation', }, ], - output: 'const a: Foo = new \n Foo \n ();', + output: `const a: Foo = new \n Foo \n ();`, }, { code: 'const a = new Foo/* comment */ /* another */();', @@ -379,7 +379,7 @@ const a = function (a = new Foo()) {}; messageId: 'preferTypeAnnotation', }, ], - output: 'const a: Foo = new Foo/* comment */ /* another */();', + output: `const a: Foo = new Foo/* comment */ /* another */();`, }, { code: 'const a = new Foo();', @@ -389,8 +389,7 @@ const a = function (a = new Foo()) {}; messageId: 'preferTypeAnnotation', }, ], - output: - 'const a: Foo = new Foo();', + output: `const a: Foo = new Foo();`, }, { code: ` diff --git a/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts b/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts index 41dd3f12e041..b0a2092994d3 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts @@ -64,7 +64,7 @@ export type W = { invalid: [ { code: noFormat`type T = { x: number; };`, - output: 'interface T { x: number; }', + output: `interface T { x: number; }`, options: ['interface'], errors: [ { @@ -76,7 +76,7 @@ export type W = { }, { code: noFormat`type T={ x: number; };`, - output: 'interface T { x: number; }', + output: `interface T { x: number; }`, options: ['interface'], errors: [ { @@ -88,7 +88,7 @@ export type W = { }, { code: noFormat`type T= { x: number; };`, - output: 'interface T { x: number; }', + output: `interface T { x: number; }`, options: ['interface'], errors: [ { @@ -120,7 +120,7 @@ export interface W { }, { code: noFormat`interface T { x: number; }`, - output: 'type T = { x: number; }', + output: `type T = { x: number; }`, options: ['type'], errors: [ { @@ -132,7 +132,7 @@ export interface W { }, { code: noFormat`interface T{ x: number; }`, - output: 'type T = { x: number; }', + output: `type T = { x: number; }`, options: ['type'], errors: [ { @@ -144,7 +144,7 @@ export interface W { }, { code: noFormat`interface T { x: number; }`, - output: 'type T = { x: number; }', + output: `type T = { x: number; }`, options: ['type'], errors: [ { @@ -156,7 +156,7 @@ export interface W { }, { code: noFormat`interface A extends B, C { x: number; };`, - output: 'type A = { x: number; } & B & C;', + output: `type A = { x: number; } & B & C;`, options: ['type'], errors: [ { @@ -168,7 +168,7 @@ export interface W { }, { code: noFormat`interface A extends B, C { x: number; };`, - output: 'type A = { x: number; } & B & C;', + output: `type A = { x: number; } & B & C;`, options: ['type'], errors: [ { diff --git a/packages/eslint-plugin/tests/rules/indent/indent.test.ts b/packages/eslint-plugin/tests/rules/indent/indent.test.ts index 49c4ce4cda30..5974cb5198ee 100644 --- a/packages/eslint-plugin/tests/rules/indent/indent.test.ts +++ b/packages/eslint-plugin/tests/rules/indent/indent.test.ts @@ -20,7 +20,7 @@ type Options = InferOptionsTypeFromRule; * Marks a test case as a plain javascript case which should be indented the same */ function nonTsTestCase(example: TemplateStringsArray): string { - return ['// Non-TS Test Case', example].join('\n'); + return [`// Non-TS Test Case`, example].join('\n'); } const individualNodeTests = [ diff --git a/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts b/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts index c926ce6a9690..c03d89ed6a61 100644 --- a/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts +++ b/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts @@ -203,7 +203,7 @@ function notcool(input: string) { { code: '(foo: undefined) => foo && console.log(foo);', errors: [{ line: 1, column: 28, messageId: 'invalidVoidExprArrow' }], - output: '(foo: undefined) => { foo && console.log(foo); };', + output: `(foo: undefined) => { foo && console.log(foo); };`, }, { code: 'foo => foo || console.log(foo);', @@ -212,12 +212,12 @@ function notcool(input: string) { { code: '(foo: undefined) => foo || console.log(foo);', errors: [{ line: 1, column: 28, messageId: 'invalidVoidExprArrow' }], - output: '(foo: undefined) => { foo || console.log(foo); };', + output: `(foo: undefined) => { foo || console.log(foo); };`, }, { code: '(foo: void) => foo || console.log(foo);', errors: [{ line: 1, column: 23, messageId: 'invalidVoidExprArrow' }], - output: '(foo: void) => { foo || console.log(foo); };', + output: `(foo: void) => { foo || console.log(foo); };`, }, { code: 'foo => (foo ? console.log(true) : console.log(false));', @@ -225,7 +225,7 @@ function notcool(input: string) { { line: 1, column: 15, messageId: 'invalidVoidExprArrow' }, { line: 1, column: 35, messageId: 'invalidVoidExprArrow' }, ], - output: 'foo => { foo ? console.log(true) : console.log(false); };', + output: `foo => { foo ? console.log(true) : console.log(false); };`, }, { code: ` diff --git a/packages/eslint-plugin/tests/rules/no-duplicate-type-constituents.test.ts b/packages/eslint-plugin/tests/rules/no-duplicate-type-constituents.test.ts index 92076923e79c..376e3fa22683 100644 --- a/packages/eslint-plugin/tests/rules/no-duplicate-type-constituents.test.ts +++ b/packages/eslint-plugin/tests/rules/no-duplicate-type-constituents.test.ts @@ -154,7 +154,7 @@ type T = Record; invalid: [ { code: 'type T = 1 | 1;', - output: 'type T = 1 ;', + output: `type T = 1 ;`, errors: [ { messageId: 'duplicate', @@ -167,7 +167,7 @@ type T = Record; }, { code: 'type T = true & true;', - output: 'type T = true ;', + output: `type T = true ;`, errors: [ { messageId: 'duplicate', @@ -180,7 +180,7 @@ type T = Record; }, { code: 'type T = null | null;', - output: 'type T = null ;', + output: `type T = null ;`, errors: [ { messageId: 'duplicate', @@ -193,7 +193,7 @@ type T = Record; }, { code: 'type T = any | any;', - output: 'type T = any ;', + output: `type T = any ;`, errors: [ { messageId: 'duplicate', @@ -206,7 +206,7 @@ type T = Record; }, { code: 'type T = { a: string | string };', - output: 'type T = { a: string };', + output: `type T = { a: string };`, errors: [ { messageId: 'duplicate', @@ -219,7 +219,7 @@ type T = Record; }, { code: 'type T = { a: string } | { a: string };', - output: 'type T = { a: string } ;', + output: `type T = { a: string } ;`, errors: [ { messageId: 'duplicate', @@ -232,7 +232,7 @@ type T = Record; }, { code: 'type T = { a: string; b: number } | { a: string; b: number };', - output: 'type T = { a: string; b: number } ;', + output: `type T = { a: string; b: number } ;`, errors: [ { messageId: 'duplicate', @@ -245,7 +245,7 @@ type T = Record; }, { code: 'type T = Set | Set;', - output: 'type T = Set ;', + output: `type T = Set ;`, errors: [ { messageId: 'duplicate', @@ -277,7 +277,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = Class | Class;', - output: 'type T = Class ;', + output: `type T = Class ;`, errors: [ { messageId: 'duplicate', @@ -290,7 +290,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = string[] | string[];', - output: 'type T = string[] ;', + output: `type T = string[] ;`, errors: [ { messageId: 'duplicate', @@ -303,7 +303,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = string[][] | string[][];', - output: 'type T = string[][] ;', + output: `type T = string[][] ;`, errors: [ { messageId: 'duplicate', @@ -316,7 +316,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = [1, 2, 3] | [1, 2, 3];', - output: 'type T = [1, 2, 3] ;', + output: `type T = [1, 2, 3] ;`, errors: [ { messageId: 'duplicate', @@ -329,7 +329,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = () => string | string;', - output: 'type T = () => string ;', + output: `type T = () => string ;`, errors: [ { messageId: 'duplicate', @@ -342,7 +342,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = () => null | null;', - output: 'type T = () => null ;', + output: `type T = () => null ;`, errors: [ { messageId: 'duplicate', @@ -355,7 +355,7 @@ type ActuallyDuplicated = IsArray ; }, { code: 'type T = (arg: string | string) => void;', - output: 'type T = (arg: string ) => void;', + output: `type T = (arg: string ) => void;`, errors: [ { messageId: 'duplicate', @@ -601,7 +601,7 @@ type T = A ; messageId: 'duplicate', data: { type: 'Union', - previous: 'A', + previous: `A`, }, }, { diff --git a/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts index 48f159aaa2cf..3711f874a3b3 100644 --- a/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts +++ b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts @@ -171,7 +171,7 @@ type Bar = Foo }, { code: 'interface Foo extends Array {}', - output: 'type Foo = Array', + output: `type Foo = Array`, errors: [ { messageId: 'noEmptyWithSuper', @@ -182,7 +182,7 @@ type Bar = Foo }, { code: 'interface Foo extends Array {}', - output: 'type Foo = Array', + output: `type Foo = Array`, errors: [ { messageId: 'noEmptyWithSuper', diff --git a/packages/eslint-plugin/tests/rules/no-non-null-asserted-optional-chain.test.ts b/packages/eslint-plugin/tests/rules/no-non-null-asserted-optional-chain.test.ts index e9308fddba6a..a9fda0210fdf 100644 --- a/packages/eslint-plugin/tests/rules/no-non-null-asserted-optional-chain.test.ts +++ b/packages/eslint-plugin/tests/rules/no-non-null-asserted-optional-chain.test.ts @@ -87,7 +87,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: '(foo?.bar).baz', + output: `(foo?.bar).baz`, }, ], }, @@ -101,7 +101,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: '(foo?.bar)().baz', + output: `(foo?.bar)().baz`, }, ], }, @@ -115,7 +115,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: '(foo?.bar)', + output: `(foo?.bar)`, }, ], }, @@ -129,7 +129,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: '(foo?.bar)()', + output: `(foo?.bar)()`, }, ], }, @@ -143,7 +143,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: '(foo?.bar)', + output: `(foo?.bar)`, }, ], }, @@ -157,7 +157,7 @@ ruleTester.run('no-non-null-asserted-optional-chain', rule, { suggestions: [ { messageId: 'suggestRemovingNonNull', - output: '(foo?.bar)()', + output: `(foo?.bar)()`, }, ], }, diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts index 248bef016ff0..89cdd1a73ed5 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -812,7 +812,7 @@ const t1 = b2 && b1 ? 'yes' : 'no'; unnecessaryConditionTest('object | true', 'alwaysTruthy'), unnecessaryConditionTest('"" | false', 'alwaysFalsy'), // Two falsy literals unnecessaryConditionTest('"always truthy"', 'alwaysTruthy'), - unnecessaryConditionTest('undefined', 'alwaysFalsy'), + unnecessaryConditionTest(`undefined`, 'alwaysFalsy'), unnecessaryConditionTest('null', 'alwaysFalsy'), unnecessaryConditionTest('void', 'alwaysFalsy'), unnecessaryConditionTest('never', 'never'), diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts index 9ec31dce3e44..2b1f6841f2c1 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-constraint.test.ts @@ -117,7 +117,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: 'const data = () => {};', + output: `const data = () => {};`, }, ], }, @@ -141,7 +141,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: 'const data = () => {};', + output: `const data = () => {};`, }, ], }, @@ -161,7 +161,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: 'const data = () => {};', + output: `const data = () => {};`, }, ], }, @@ -181,7 +181,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: 'const data = () => {};', + output: `const data = () => {};`, }, ], }, @@ -205,7 +205,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: 'const data = () => {};', + output: `const data = () => {};`, }, ], }, @@ -229,7 +229,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: 'const data = () => {};', + output: `const data = () => {};`, }, ], }, @@ -253,7 +253,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: 'const data = () => {};', + output: `const data = () => {};`, }, ], }, @@ -301,7 +301,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: 'const data = () => {};', + output: `const data = () => {};`, }, ], }, @@ -315,7 +315,7 @@ function data() {} { messageId: 'removeUnnecessaryConstraint', data: { constraint: 'any' }, - output: 'const data = () => {};', + output: `const data = () => {};`, }, ], }, diff --git a/packages/eslint-plugin/tests/rules/quotes.test.ts b/packages/eslint-plugin/tests/rules/quotes.test.ts index 1c718dab212a..d6c25e6f41ae 100644 --- a/packages/eslint-plugin/tests/rules/quotes.test.ts +++ b/packages/eslint-plugin/tests/rules/quotes.test.ts @@ -688,7 +688,7 @@ abstract class Foo { { code: '

', output: "
", - options: ['single'], + options: [`single`], parserOptions: { ecmaFeatures: { jsx: true, diff --git a/packages/eslint-plugin/tests/rules/semi.test.ts b/packages/eslint-plugin/tests/rules/semi.test.ts index 4db70d358b64..e1f53043a9ce 100644 --- a/packages/eslint-plugin/tests/rules/semi.test.ts +++ b/packages/eslint-plugin/tests/rules/semi.test.ts @@ -229,7 +229,7 @@ class PanCamera extends FreeCamera { `, // https://github.com/typescript-eslint/typescript-eslint/issues/123 'export default interface test {}', - 'declare function declareFn(): string;', + `declare function declareFn(): string;`, // ESLint 'var x = 5;', 'var x =5, y;', @@ -739,7 +739,7 @@ class PanCamera extends FreeCamera { ...[ { - code: 'declare function declareFn(): string;', + code: `declare function declareFn(): string;`, errors: [ { line: 1, diff --git a/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts b/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts index 6af33968aab1..e1a2afc38288 100644 --- a/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts +++ b/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts @@ -366,7 +366,7 @@ type T = 1 | string | {} | A; ...invalid('&'), { code: 'type T = (B | C) & A;', - output: 'type T = A & (B | C);', + output: `type T = A & (B | C);`, errors: [ { messageId: 'notSortedNamed', diff --git a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts index 37b6bb0de44f..9a285b70d186 100644 --- a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts @@ -701,7 +701,7 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareStringLength', - output: ' declare const x: string; if (x.length > 0) {}', + output: ` declare const x: string; if (x.length > 0) {}`, }, { messageId: 'conditionFixCompareEmptyString', @@ -709,7 +709,7 @@ if (y) { }, { messageId: 'conditionFixCastBoolean', - output: ' declare const x: string; if (Boolean(x)) {}', + output: ` declare const x: string; if (Boolean(x)) {}`, }, ], }, @@ -720,7 +720,7 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareStringLength', - output: ' (x: string) => (x.length === 0);', + output: ` (x: string) => (x.length === 0);`, }, { messageId: 'conditionFixCompareEmptyString', @@ -728,7 +728,7 @@ if (y) { }, { messageId: 'conditionFixCastBoolean', - output: ' (x: string) => (!Boolean(x));', + output: ` (x: string) => (!Boolean(x));`, }, ], }, @@ -739,8 +739,7 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareStringLength', - output: - ' (x: T) => (x.length > 0) ? 1 : 0;', + output: ` (x: T) => (x.length > 0) ? 1 : 0;`, }, { messageId: 'conditionFixCompareEmptyString', @@ -748,8 +747,7 @@ if (y) { }, { messageId: 'conditionFixCastBoolean', - output: - ' (x: T) => (Boolean(x)) ? 1 : 0;', + output: ` (x: T) => (Boolean(x)) ? 1 : 0;`, }, ], }, @@ -777,16 +775,16 @@ if (y) { { messageId: 'conditionFixCompareZero', // TODO: fix compare zero suggestion for bigint - output: 'while (0n !== 0) {}', + output: `while (0n !== 0) {}`, }, { // TODO: remove check NaN suggestion for bigint messageId: 'conditionFixCompareNaN', - output: 'while (!Number.isNaN(0n)) {}', + output: `while (!Number.isNaN(0n)) {}`, }, { messageId: 'conditionFixCastBoolean', - output: 'while (Boolean(0n)) {}', + output: `while (Boolean(0n)) {}`, }, ], }, @@ -797,15 +795,15 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareZero', - output: ' for (; 123 !== 0;) {}', + output: ` for (; 123 !== 0;) {}`, }, { messageId: 'conditionFixCompareNaN', - output: ' for (; !Number.isNaN(123);) {}', + output: ` for (; !Number.isNaN(123);) {}`, }, { messageId: 'conditionFixCastBoolean', - output: ' for (; Boolean(123);) {}', + output: ` for (; Boolean(123);) {}`, }, ], }, @@ -816,16 +814,15 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareZero', - output: ' declare const x: number; if (x !== 0) {}', + output: ` declare const x: number; if (x !== 0) {}`, }, { messageId: 'conditionFixCompareNaN', - output: - ' declare const x: number; if (!Number.isNaN(x)) {}', + output: ` declare const x: number; if (!Number.isNaN(x)) {}`, }, { messageId: 'conditionFixCastBoolean', - output: ' declare const x: number; if (Boolean(x)) {}', + output: ` declare const x: number; if (Boolean(x)) {}`, }, ], }, @@ -837,16 +834,16 @@ if (y) { { messageId: 'conditionFixCompareZero', // TODO: fix compare zero suggestion for bigint - output: ' (x: bigint) => x === 0;', + output: ` (x: bigint) => x === 0;`, }, { // TODO: remove check NaN suggestion for bigint messageId: 'conditionFixCompareNaN', - output: ' (x: bigint) => Number.isNaN(x);', + output: ` (x: bigint) => Number.isNaN(x);`, }, { messageId: 'conditionFixCastBoolean', - output: ' (x: bigint) => !Boolean(x);', + output: ` (x: bigint) => !Boolean(x);`, }, ], }, @@ -857,17 +854,15 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareZero', - output: ' (x: T) => (x !== 0) ? 1 : 0;', + output: ` (x: T) => (x !== 0) ? 1 : 0;`, }, { messageId: 'conditionFixCompareNaN', - output: - ' (x: T) => (!Number.isNaN(x)) ? 1 : 0;', + output: ` (x: T) => (!Number.isNaN(x)) ? 1 : 0;`, }, { messageId: 'conditionFixCastBoolean', - output: - ' (x: T) => (Boolean(x)) ? 1 : 0;', + output: ` (x: T) => (Boolean(x)) ? 1 : 0;`, }, ], }, @@ -897,18 +892,15 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareZero', - output: - ' declare const a: any[] & { notLength: number }; if (a.notLength !== 0) {}', + output: ` declare const a: any[] & { notLength: number }; if (a.notLength !== 0) {}`, }, { messageId: 'conditionFixCompareNaN', - output: - ' declare const a: any[] & { notLength: number }; if (!Number.isNaN(a.notLength)) {}', + output: ` declare const a: any[] & { notLength: number }; if (!Number.isNaN(a.notLength)) {}`, }, { messageId: 'conditionFixCastBoolean', - output: - ' declare const a: any[] & { notLength: number }; if (Boolean(a.notLength)) {}', + output: ` declare const a: any[] & { notLength: number }; if (Boolean(a.notLength)) {}`, }, ], }, @@ -966,11 +958,11 @@ if (y) { suggestions: [ { messageId: 'conditionFixDefaultFalse', - output: 'declare const x: boolean | null; if (x ?? false) {}', + output: `declare const x: boolean | null; if (x ?? false) {}`, }, { messageId: 'conditionFixCompareTrue', - output: 'declare const x: boolean | null; if (x === true) {}', + output: `declare const x: boolean | null; if (x === true) {}`, }, ], }, @@ -981,11 +973,11 @@ if (y) { suggestions: [ { messageId: 'conditionFixDefaultFalse', - output: ' (x?: boolean) => !(x ?? false);', + output: ` (x?: boolean) => !(x ?? false);`, }, { messageId: 'conditionFixCompareFalse', - output: ' (x?: boolean) => x === false;', + output: ` (x?: boolean) => x === false;`, }, ], }, @@ -996,13 +988,11 @@ if (y) { suggestions: [ { messageId: 'conditionFixDefaultFalse', - output: - ' (x: T) => (x ?? false) ? 1 : 0;', + output: ` (x: T) => (x ?? false) ? 1 : 0;`, }, { messageId: 'conditionFixCompareTrue', - output: - ' (x: T) => (x === true) ? 1 : 0;', + output: ` (x: T) => (x === true) ? 1 : 0;`, }, ], }, @@ -1036,7 +1026,7 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareNullish', - output: ' (x?: { a: number }) => x == null;', + output: ` (x?: { a: number }) => x == null;`, }, ], }, @@ -1047,8 +1037,7 @@ if (y) { suggestions: [ { messageId: 'conditionFixCompareNullish', - output: - ' (x: T) => (x != null) ? 1 : 0;', + output: ` (x: T) => (x != null) ? 1 : 0;`, }, ], }, diff --git a/packages/eslint-plugin/tests/schemas.test.ts b/packages/eslint-plugin/tests/schemas.test.ts index 4da86cdab81a..5524203d1815 100644 --- a/packages/eslint-plugin/tests/schemas.test.ts +++ b/packages/eslint-plugin/tests/schemas.test.ts @@ -180,7 +180,7 @@ describe('Rule schemas should validate options correctly', () => { test(`${ruleName} rejects arbitrary options`, () => { if (areOptionsValid(rule, [{ 'arbitrary-schemas.test.ts': true }])) { - throw new Error('Options succeeded validation for arbitrary options'); + throw new Error(`Options succeeded validation for arbitrary options`); } }); } From 4b2f9e1ee2c13da914b1e3321002a2b2bc43cbc7 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Tue, 12 Dec 2023 08:53:07 +0200 Subject: [PATCH 15/17] more reverts --- packages/eslint-plugin/tests/docs.test.ts | 2 +- packages/eslint-plugin/tests/rules/no-extra-parens.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index 8e0739aed38d..a811396013cd 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -98,7 +98,7 @@ describe('Validating rule docs', () => { }); }); - test('headings must be title-cased', () => { + test(`headings must be title-cased`, () => { // Get all H2 headings objects as the other levels are variable by design. const headings = tokens.filter(tokenIsH2); diff --git a/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts b/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts index 714dce655ba8..94c796a98a8e 100644 --- a/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts +++ b/packages/eslint-plugin/tests/rules/no-extra-parens.test.ts @@ -731,7 +731,7 @@ f<(number) | string>(1) }, { - code: noFormat` + code: ` const Component = (

From a9710816ed5e4bf875bdcda3e19c55ea58cd9aab Mon Sep 17 00:00:00 2001 From: StyleShit Date: Tue, 12 Dec 2023 09:14:37 +0200 Subject: [PATCH 16/17] wip --- .../no-useless-template-literals.test.ts | 195 ++++++++---------- 1 file changed, 81 insertions(+), 114 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts index 8cbd16179965..162d7a0928a1 100644 --- a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts +++ b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts @@ -16,255 +16,222 @@ const ruleTester = new RuleTester({ ruleTester.run('no-useless-template-literals', rule, { valid: [ "const string = 'a';", - 'const string = `a`;', ` - const string = 'a'; - const concatenated = \`\${string}b\`; + declare const string: 'a'; + \`\${string}b\`; `, ` - const number = 1; - const concatenated = \`\${number}b\`; + declare const number: 1; + \`\${number}b\`; `, ` - const boolean = true; - const concatenated = \`\${boolean}b\`; + declare const boolean: true; + \`\${boolean}b\`; `, ` - const nullish = null; - const concatenated = \`\${nullish}-undefined\`; + declare const nullish: null; + \`\${nullish}-undefined\`; `, ` - const left = 'a'; - const right = 'b'; - const concatenated = \`\${left}\${right}\`; + declare const left: 'a'; + declare const right: 'b'; + \`\${left}\${right}\`; `, ` - const left = 'a'; - const right = 'c'; - const concatenated = \`\${left}b\${right}\`; + declare const left: 'a'; + declare const right: 'c'; + \`\${left}b\${right}\`; `, ` - const left = 'a'; - const center = 'b'; - const right = 'c'; - const concatenated = \`\${left}\${center}\${right}\`; + declare const left: 'a'; + declare const center: 'b'; + declare const right: 'c'; + \`\${left}\${center}\${right}\`; `, - ` - const concatenated = \`1 + 1 = \${1 + 1}\`; - `, + '`1 + 1 = ${1 + 1}`;', - ` - const concatenated = \`true && false = \${true && false}\`; - `, + '`true && false = ${true && false}`;', - ` - tag\`\${'a'}\${'b'}\`; - `, + "tag`${'a'}${'b'}`;", - ` - const wrappedNumber = \`\${1}\`; - `, + '`${1}`;', - ` - const wrappedBigint = \`\${1n}\`; - `, + '`${1n}`;', - ` - const wrappedBoolean = \`\${true}\`; - `, + '`${true}`;', - ` - const wrappedNull = \`\${null}\`; - `, + '`${null}`;', - ` - const wrappedUndefined = \`\${undefined}\`; - `, + '`${undefined}`;', - ` - const wrappedFunction = \`\${function () {}}\`; - `, + '`${function () {}}`;', - ` - const wrappedArrowFunction = \`\${() => {}}\`; - `, + '`${() => {}}`;', - ` - const wrappedFunctionWithArgs = \`\${(...args: any[]) => args}\`; - `, + '`${(...args: any[]) => args}`;', ` - const number = 1; - const wrapped = \`\${number}\`; + declare const number: 1; + \`\${number}\`; `, ` - const boolean = true; - const wrapped = \`\${boolean}\`; + declare const boolean: true; + \`\${boolean}\`; `, ` - const nullish = null; - const wrapped = \`\${nullish}\`; + declare const nullish: null; + \`\${nullish}\`; `, ` declare const union: string | number; - const wrapped = \`\${union}\`; + \`\${union}\`; `, ` declare const unknown: unknown; - const wrapped = \`\${unknown}\`; + \`\${unknown}\`; `, ` declare const never: never; - const wrapped = \`\${never}\`; + \`\${never}\`; `, ` declare const any: any; - const wrapped = \`\${any}\`; + \`\${any}\`; `, ` function func(arg: T) { - const wrapped = \`\${arg}\`; + \`\${arg}\`; } `, ` - const wrapped = \`with + \`with new line\`; `, ` - const a = 'a'; + declare const a: 'a'; - const wrapped = \`\${a} with + \`\${a} with new line\`; `, noFormat` - const wrapped = \`with windows \r new line\`; + \`with windows \r new line\`; `, ], invalid: [ { - code: ` - const concatenated = \`\${'a'}\${'b'}\`; - `, + code: "`${'a'}${'b'}`;", errors: [ { messageId: 'noUselessTemplateLiteral', - line: 2, - column: 33, - endColumn: 36, + line: 1, + column: 4, + endColumn: 7, }, { messageId: 'noUselessTemplateLiteral', - line: 2, - column: 39, - endColumn: 42, + line: 1, + column: 10, + endColumn: 13, }, ], }, { code: ` - const b = 'b'; - const concatenated = \`a\${b}\${'c'}\`; + declare const b: 'b'; + \`a\${b}\${'c'}\`; `, errors: [ { messageId: 'noUselessTemplateLiteral', line: 3, - column: 38, - endColumn: 41, + column: 17, + endColumn: 20, }, ], }, { - code: ` - const concatenated = \`a\${'b'}\`; - `, + code: "`a${'b'}`;", errors: [ { messageId: 'noUselessTemplateLiteral', - line: 2, - column: 34, - endColumn: 37, + line: 1, + column: 5, + endColumn: 8, }, ], }, { - code: ` - const concatenated = \`\${'1 + 1 = '}\${2}\`; - `, + code: "`${'1 + 1 = '}${2}`;", errors: [ { messageId: 'noUselessTemplateLiteral', - line: 2, - column: 33, - endColumn: 43, + line: 1, + column: 4, + endColumn: 14, }, ], }, { - code: ` - const concatenated = \`\${'a'}\${true}\`; - `, + code: "`${'a'}${true}`;", errors: [ { messageId: 'noUselessTemplateLiteral', - line: 2, - column: 33, - endColumn: 36, + line: 1, + column: 4, + endColumn: 7, }, ], }, { code: ` - const string = 'a'; - const wrapped = \`\${string}\`; + declare const string: 'a'; + \`\${string}\`; `, errors: [ { messageId: 'noUselessTemplateLiteral', line: 3, - column: 28, - endColumn: 34, + column: 12, + endColumn: 18, }, ], }, { - code: ` - const wrappedSymbol = \`\${String(Symbol.for('test'))}\`; - `, + code: "`${String(Symbol.for('test'))}`;", errors: [ { messageId: 'noUselessTemplateLiteral', - line: 2, - column: 34, - endColumn: 60, + line: 1, + column: 4, + endColumn: 30, }, ], }, @@ -272,14 +239,14 @@ ruleTester.run('no-useless-template-literals', rule, { { code: ` declare const intersection: string & { _brand: 'test-brand' }; - const wrapped = \`\${intersection}\`; + \`\${intersection}\`; `, errors: [ { messageId: 'noUselessTemplateLiteral', line: 3, - column: 28, - endColumn: 40, + column: 12, + endColumn: 24, }, ], }, @@ -287,15 +254,15 @@ ruleTester.run('no-useless-template-literals', rule, { { code: ` function func(arg: T) { - const wrapped = \`\${arg}\`; + \`\${arg}\`; } `, errors: [ { messageId: 'noUselessTemplateLiteral', line: 3, - column: 30, - endColumn: 33, + column: 14, + endColumn: 17, }, ], }, From 408e5c721d6e00c7c329d0d355171c67a5fcb2c3 Mon Sep 17 00:00:00 2001 From: StyleShit Date: Tue, 12 Dec 2023 18:56:06 +0200 Subject: [PATCH 17/17] wip --- .../rules/no-useless-template-literals.md | 4 +- .../src/rules/no-useless-template-literals.ts | 16 ++-- .../no-useless-template-literals.test.ts | 82 ++++++++++++++++--- 3 files changed, 81 insertions(+), 21 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-useless-template-literals.md b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md index 9a5c780394e6..f641e8d1f823 100644 --- a/packages/eslint-plugin/docs/rules/no-useless-template-literals.md +++ b/packages/eslint-plugin/docs/rules/no-useless-template-literals.md @@ -35,9 +35,9 @@ const wrappedIntersection = `${intersectionWithString}`; const ab1 = 'ab'; const ab2 = 'ab'; -const stringWithNumber = `1 + 1 = ${2}`; +const stringWithNumber = `1 + 1 = 2`; -const stringWithBoolean = `true is ${true}`; +const stringWithBoolean = `true is true`; const text = 'a'; const wrappedText = text; diff --git a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts index a4843ed17d77..48c714edb90a 100644 --- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -7,6 +7,7 @@ import { getConstrainedTypeAtLocation, getParserServices, isTypeFlagSet, + isUndefinedIdentifier, } from '../util'; type MessageId = 'noUselessTemplateLiteral'; @@ -72,18 +73,15 @@ export default createRule<[], MessageId>({ return; } - const stringLiteralExpressions = node.expressions.filter( - (expression): expression is TSESTree.StringLiteral => { - return ( - isUnderlyingTypeString(expression) && - expression.type === AST_NODE_TYPES.Literal - ); - }, + const literalsOrUndefinedExpressions = node.expressions.filter( + (expression): expression is TSESTree.Literal | TSESTree.Identifier => + expression.type === AST_NODE_TYPES.Literal || + isUndefinedIdentifier(expression), ); - stringLiteralExpressions.forEach(stringLiteral => { + literalsOrUndefinedExpressions.forEach(expression => { context.report({ - node: stringLiteral, + node: expression, messageId: 'noUselessTemplateLiteral', }); }); diff --git a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts index 162d7a0928a1..54ac89c2c797 100644 --- a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts +++ b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts @@ -38,6 +38,11 @@ ruleTester.run('no-useless-template-literals', rule, { \`\${nullish}-undefined\`; `, + ` + declare const undefinedish: undefined; + \`\${undefinedish}\`; + `, + ` declare const left: 'a'; declare const right: 'b'; @@ -63,16 +68,6 @@ ruleTester.run('no-useless-template-literals', rule, { "tag`${'a'}${'b'}`;", - '`${1}`;', - - '`${1n}`;', - - '`${true}`;', - - '`${null}`;', - - '`${undefined}`;', - '`${function () {}}`;', '`${() => {}}`;', @@ -140,6 +135,61 @@ ruleTester.run('no-useless-template-literals', rule, { ], invalid: [ + { + code: '`${1}`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 1, + column: 4, + endColumn: 5, + }, + ], + }, + { + code: '`${1n}`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 1, + column: 4, + endColumn: 6, + }, + ], + }, + { + code: '`${true}`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 1, + column: 4, + endColumn: 8, + }, + ], + }, + { + code: '`${null}`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 1, + column: 4, + endColumn: 8, + }, + ], + }, + { + code: '`${undefined}`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + line: 1, + column: 4, + endColumn: 13, + }, + ], + }, { code: "`${'a'}${'b'}`;", errors: [ @@ -194,6 +244,12 @@ ruleTester.run('no-useless-template-literals', rule, { column: 4, endColumn: 14, }, + { + messageId: 'noUselessTemplateLiteral', + line: 1, + column: 17, + endColumn: 18, + }, ], }, @@ -206,6 +262,12 @@ ruleTester.run('no-useless-template-literals', rule, { column: 4, endColumn: 7, }, + { + messageId: 'noUselessTemplateLiteral', + line: 1, + column: 10, + endColumn: 14, + }, ], },