From 30efa1262b1285efcf2f458af629d14950379bd4 Mon Sep 17 00:00:00 2001 From: Adam Jones Date: Wed, 1 Mar 2023 15:00:21 +0000 Subject: [PATCH 1/4] feat(eslint-plugin): add allowNever support to restrict-template-expressions --- .../rules/restrict-template-expressions.md | 9 ++++ .../rules/restrict-template-expressions.ts | 10 +++++ .../restrict-template-expressions.test.ts | 41 ++++++++++++++++++- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md index e2d18d8e267..d7afae0ee26 100644 --- a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md +++ b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md @@ -90,6 +90,15 @@ const arg = /foo/; const msg1 = `arg = ${arg}`; ``` +### `allowNever` + +Examples of additional **correct** code for this rule with `{ allowNever: true }`: + +```ts +const arg = 'something'; +const msg1 = typeof arg === 'string' ? arg : `arg = ${arg}`; +``` + ## Related To - [`no-base-to-string`](./no-base-to-string.md) diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts index 382f8ce0137..485ba42378b 100644 --- a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts +++ b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts @@ -11,6 +11,7 @@ type Options = [ allowAny?: boolean; allowNullish?: boolean; allowRegExp?: boolean; + allowNever?: boolean; }, ]; @@ -58,6 +59,11 @@ export default util.createRule({ 'Whether to allow `regexp` typed values in template expressions.', type: 'boolean', }, + allowNever: { + description: + 'Whether to allow `never` typed values in template expressions.', + type: 'boolean', + }, }, }, ], @@ -111,6 +117,10 @@ export default util.createRule({ return true; } + if (options.allowNever && util.isTypeNeverType(type)) { + return true; + } + return false; } diff --git a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts index 7e80bdbdf5d..50cd7cb6b70 100644 --- a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts @@ -227,6 +227,44 @@ ruleTester.run('restrict-template-expressions', rule, { } `, }, + // allowNever + { + options: [{ allowNever: true }], + code: ` + const arg = 'hello'; + const msg = typeof arg === 'string' ? arg : \`arg = \${arg}\`; + `, + }, + { + options: [{ allowNever: true }], + code: ` + function test(arg: 'one' | 'two') { + switch (arg) { + case 'one': + return 1; + case 'two': + return 2; + default: + throw new Error(\`Unrecognised arg: \${arg}\`); + } + } + `, + }, + { + options: [{ allowNever: true }], + code: ` + // more variants may be added to Foo in the future + type Foo = { type: 'a'; value: number }; + + function checkFoosAreMatching(foo1: Foo, foo2: Foo) { + if (foo1.type !== foo2.type) { + // since Foo currently only has one variant, this code is never run, and \`foo1.type\` has type \`never\`. + throw new Error(\`expected \${foo1.type}, found \${foo2.type}\`); + // ^ Invalid type "never" of template literal expression. + } + } + `, + }, // allow ALL { options: [ @@ -235,10 +273,11 @@ ruleTester.run('restrict-template-expressions', rule, { allowBoolean: true, allowNullish: true, allowRegExp: true, + allowNever: true, }, ], code: ` - type All = string | number | boolean | null | undefined | RegExp; + type All = string | number | boolean | null | undefined | RegExp | never; function test(arg: T) { return \`arg = \${arg}\`; } From bc3d0610d2eed4839d5d06810cdaf129d286adf9 Mon Sep 17 00:00:00 2001 From: Adam Jones Date: Sat, 11 Mar 2023 14:52:53 +0000 Subject: [PATCH 2/4] Update tests --- .../restrict-template-expressions.test.ts | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts index 50cd7cb6b70..f7b342ce199 100644 --- a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts @@ -228,6 +228,13 @@ ruleTester.run('restrict-template-expressions', rule, { `, }, // allowNever + { + options: [{ allowNever: true }], + code: ` + declare const value: never; + const stringy = \`\${value}\`; + `, + }, { options: [{ allowNever: true }], code: ` @@ -255,12 +262,11 @@ ruleTester.run('restrict-template-expressions', rule, { code: ` // more variants may be added to Foo in the future type Foo = { type: 'a'; value: number }; - + function checkFoosAreMatching(foo1: Foo, foo2: Foo) { if (foo1.type !== foo2.type) { // since Foo currently only has one variant, this code is never run, and \`foo1.type\` has type \`never\`. throw new Error(\`expected \${foo1.type}, found \${foo2.type}\`); - // ^ Invalid type "never" of template literal expression. } } `, @@ -457,6 +463,21 @@ ruleTester.run('restrict-template-expressions', rule, { }, ], }, + { + options: [{ allowNever: false }], + code: ` + declare const value: never; + const stringy = \`\${value}\`; + `, + errors: [ + { + messageId: 'invalidType', + data: { type: 'never' }, + line: 3, + column: 26, + }, + ], + }, // TS 3.9 change { options: [{ allowAny: true }], From 5ba5a9959a8fd440984203b49e176a2626394038 Mon Sep 17 00:00:00 2001 From: Adam Jones Date: Tue, 14 Mar 2023 21:22:32 +0000 Subject: [PATCH 3/4] Fix lint issues --- .../tests/rules/restrict-template-expressions.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts index f7b342ce199..3ae3a3ed4c9 100644 --- a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts @@ -231,8 +231,8 @@ ruleTester.run('restrict-template-expressions', rule, { { options: [{ allowNever: true }], code: ` - declare const value: never; - const stringy = \`\${value}\`; + declare const value: never; + const stringy = \`\${value}\`; `, }, { @@ -466,8 +466,8 @@ ruleTester.run('restrict-template-expressions', rule, { { options: [{ allowNever: false }], code: ` - declare const value: never; - const stringy = \`\${value}\`; + declare const value: never; + const stringy = \`\${value}\`; `, errors: [ { From 0a97df74ba65395ad467026a4df38c552d3b0ef1 Mon Sep 17 00:00:00 2001 From: Adam Jones Date: Sun, 19 Mar 2023 14:57:13 +0000 Subject: [PATCH 4/4] Fix test --- .../tests/rules/restrict-template-expressions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts index 3ae3a3ed4c9..b58305051fa 100644 --- a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts @@ -474,7 +474,7 @@ ruleTester.run('restrict-template-expressions', rule, { messageId: 'invalidType', data: { type: 'never' }, line: 3, - column: 26, + column: 28, }, ], },