diff --git a/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md b/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md index 9320624924b9..11998bfc5f23 100644 --- a/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md +++ b/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md @@ -1,18 +1,22 @@ --- -description: 'Require switch-case statements to be exhaustive with union type.' +description: 'Require switch-case statements to be exhaustive with union types and enums.' --- > 🛑 This file is source code, not the primary documentation location! 🛑 > > See **https://typescript-eslint.io/rules/switch-exhaustiveness-check** for documentation. -When working with union types in TypeScript, it's common to want to write a `switch` statement intended to contain a `case` for each constituent (possible type in the union). -However, if the union type changes, it's easy to forget to modify the cases to account for any new types. +When working with union types or enums in TypeScript, it's common to want to write a `switch` statement intended to contain a `case` for each constituent (possible type in the union or the enum). +However, if the union type or the enum changes, it's easy to forget to modify the cases to account for any new types. -This rule reports when a `switch` statement over a value typed as a union of literals is missing a case for any of those literal types and does not have a `default` clause. +This rule reports when a `switch` statement over a value typed as a union of literals or as an enum is missing a case for any of those literal types and does not have a `default` clause. ## Examples +When the switch doesn't have exhaustive cases, either filling them all out or adding a default will correct the rule's complaint. + +Here are some examples of code working with a union of literals: + ### ❌ Incorrect @@ -27,7 +31,7 @@ type Day = | 'Saturday' | 'Sunday'; -const day = 'Monday' as Day; +declare const day: Day; let result = 0; switch (day) { @@ -49,7 +53,7 @@ type Day = | 'Saturday' | 'Sunday'; -const day = 'Monday' as Day; +declare const day: Day; let result = 0; switch (day) { @@ -89,7 +93,7 @@ type Day = | 'Saturday' | 'Sunday'; -const day = 'Monday' as Day; +declare const day: Day; let result = 0; switch (day) { @@ -101,6 +105,80 @@ switch (day) { } ``` + + +Likewise, here are some examples of code working with an enum: + + + +### ❌ Incorrect + +```ts +enum Fruit { + Apple, + Banana, + Cherry, +} + +declare const fruit: Fruit; + +switch (fruit) { + case Fruit.Apple: + console.log('an apple'); + break; +} +``` + +### ✅ Correct + +```ts +enum Fruit { + Apple, + Banana, + Cherry, +} + +declare const fruit: Fruit; + +switch (fruit) { + case Fruit.Apple: + console.log('an apple'); + break; + + case Fruit.Banana: + console.log('a banana'); + break; + + case Fruit.Cherry: + console.log('a cherry'); + break; +} +``` + +### ✅ Correct + +```ts +enum Fruit { + Apple, + Banana, + Cherry, +} + +declare const fruit: Fruit; + +switch (fruit) { + case Fruit.Apple: + console.log('an apple'); + break; + + default: + console.log('a fruit'); + break; +} +``` + + + ## When Not To Use It -If you don't frequently `switch` over union types with many parts, or intentionally wish to leave out some parts. +If you don't frequently `switch` over union types or enums with many parts, or intentionally wish to leave out some parts. diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts index 6abdbf27fc6e..fc3f30753ed8 100644 --- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts +++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts @@ -17,7 +17,7 @@ export default createRule({ type: 'suggestion', docs: { description: - 'Require switch-case statements to be exhaustive with union type', + 'Require switch-case statements to be exhaustive with union types and enums', requiresTypeChecking: true, }, hasSuggestions: true, @@ -73,13 +73,19 @@ export default createRule({ (missingBranchName || missingBranchName === '') && requiresQuoting(missingBranchName.toString(), compilerOptions.target) ) { - caseTest = `${symbolName}['${missingBranchName}']`; + const escapedBranchName = missingBranchName + .replace(/'/g, "\\'") + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r'); + + caseTest = `${symbolName}['${escapedBranchName}']`; } const errorMessage = `Not implemented yet: ${caseTest} case`; + const escapedErrorMessage = errorMessage.replace(/'/g, "\\'"); missingCases.push( - `case ${caseTest}: { throw new Error('${errorMessage}') }`, + `case ${caseTest}: { throw new Error('${escapedErrorMessage}') }`, ); } diff --git a/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts b/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts index b250a09e2bb2..9852e0d543df 100644 --- a/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts +++ b/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts @@ -1,4 +1,4 @@ -import { RuleTester } from '@typescript-eslint/rule-tester'; +import { noFormat, RuleTester } from '@typescript-eslint/rule-tester'; import path from 'path'; import switchExhaustivenessCheck from '../../src/rules/switch-exhaustiveness-check'; @@ -518,7 +518,7 @@ export enum Enum { function test(arg: Enum): string { switch (arg) { - case Enum['test-test']: { throw new Error('Not implemented yet: Enum['test-test'] case') } + case Enum['test-test']: { throw new Error('Not implemented yet: Enum[\\'test-test\\'] case') } case Enum.test: { throw new Error('Not implemented yet: Enum.test case') } } } @@ -555,7 +555,7 @@ export enum Enum { function test(arg: Enum): string { switch (arg) { - case Enum['']: { throw new Error('Not implemented yet: Enum[''] case') } + case Enum['']: { throw new Error('Not implemented yet: Enum[\\'\\'] case') } case Enum.test: { throw new Error('Not implemented yet: Enum.test case') } } } @@ -592,7 +592,7 @@ export enum Enum { function test(arg: Enum): string { switch (arg) { - case Enum['9test']: { throw new Error('Not implemented yet: Enum['9test'] case') } + case Enum['9test']: { throw new Error('Not implemented yet: Enum[\\'9test\\'] case') } case Enum.test: { throw new Error('Not implemented yet: Enum.test case') } } } @@ -602,5 +602,80 @@ function test(arg: Enum): string { }, ], }, + { + code: ` + enum Enum { + 'a' = 1, + [\`key-with + + new-line\`] = 2, + } + + declare const a: Enum; + + switch (a) { + } + `, + errors: [ + { + messageId: 'switchIsNotExhaustive', + suggestions: [ + { + messageId: 'addMissingCases', + output: ` + enum Enum { + 'a' = 1, + [\`key-with + + new-line\`] = 2, + } + + declare const a: Enum; + + switch (a) { + case Enum.a: { throw new Error('Not implemented yet: Enum.a case') } + case Enum['key-with\\n\\n new-line']: { throw new Error('Not implemented yet: Enum[\\'key-with\\n\\n new-line\\'] case') } + } + `, + }, + ], + }, + ], + }, + { + code: noFormat` + enum Enum { + 'a' = 1, + "'a' \`b\` \\"c\\"" = 2, + } + + declare const a: Enum; + + switch (a) {} + `, + errors: [ + { + messageId: 'switchIsNotExhaustive', + suggestions: [ + { + messageId: 'addMissingCases', + output: ` + enum Enum { + 'a' = 1, + "'a' \`b\` \\"c\\"" = 2, + } + + declare const a: Enum; + + switch (a) { + case Enum.a: { throw new Error('Not implemented yet: Enum.a case') } + case Enum['\\'a\\' \`b\` "c"']: { throw new Error('Not implemented yet: Enum[\\'\\\\'a\\\\' \`b\` "c"\\'] case') } + } + `, + }, + ], + }, + ], + }, ], });
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: