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, + }, ], }, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy