Skip to content

Commit 98ab010

Browse files
authored
fix(eslint-plugin): [switch-exhaustiveness-check] handle special characters in enum keys (typescript-eslint#2207)
1 parent 742b679 commit 98ab010

File tree

2 files changed

+149
-2
lines changed

2 files changed

+149
-2
lines changed

packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,25 @@ export default createRule({
3232
const sourceCode = context.getSourceCode();
3333
const service = getParserServices(context);
3434
const checker = service.program.getTypeChecker();
35+
const compilerOptions = service.program.getCompilerOptions();
36+
37+
function requiresQuoting(name: string): boolean {
38+
if (name.length === 0) {
39+
return true;
40+
}
41+
42+
if (!ts.isIdentifierStart(name.charCodeAt(0), compilerOptions.target)) {
43+
return true;
44+
}
45+
46+
for (let i = 1; i < name.length; i += 1) {
47+
if (!ts.isIdentifierPart(name.charCodeAt(i), compilerOptions.target)) {
48+
return true;
49+
}
50+
}
51+
52+
return false;
53+
}
3554

3655
function getNodeType(node: TSESTree.Node): ts.Type {
3756
const tsNode = service.esTreeNodeToTSNodeMap.get(node);
@@ -42,6 +61,7 @@ export default createRule({
4261
fixer: TSESLint.RuleFixer,
4362
node: TSESTree.SwitchStatement,
4463
missingBranchTypes: Array<ts.Type>,
64+
symbolName?: string,
4565
): TSESLint.RuleFix | null {
4666
const lastCase =
4767
node.cases.length > 0 ? node.cases[node.cases.length - 1] : null;
@@ -67,7 +87,17 @@ export default createRule({
6787
continue;
6888
}
6989

70-
const caseTest = checker.typeToString(missingBranchType);
90+
const missingBranchName = missingBranchType.getSymbol()?.escapedName;
91+
let caseTest = checker.typeToString(missingBranchType);
92+
93+
if (
94+
symbolName &&
95+
(missingBranchName || missingBranchName === '') &&
96+
requiresQuoting(missingBranchName.toString())
97+
) {
98+
caseTest = `${symbolName}['${missingBranchName}']`;
99+
}
100+
71101
const errorMessage = `Not implemented yet: ${caseTest} case`;
72102

73103
missingCases.push(
@@ -101,6 +131,7 @@ export default createRule({
101131

102132
function checkSwitchExhaustive(node: TSESTree.SwitchStatement): void {
103133
const discriminantType = getNodeType(node.discriminant);
134+
const symbolName = discriminantType.getSymbol()?.escapedName;
104135

105136
if (discriminantType.isUnion()) {
106137
const unionTypes = unionTypeParts(discriminantType);
@@ -139,7 +170,12 @@ export default createRule({
139170
{
140171
messageId: 'addMissingCases',
141172
fix(fixer): TSESLint.RuleFix | null {
142-
return fixSwitch(fixer, node, missingBranchTypes);
173+
return fixSwitch(
174+
fixer,
175+
node,
176+
missingBranchTypes,
177+
symbolName?.toString(),
178+
);
143179
},
144180
},
145181
],

packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,117 @@ function test(value: T): number {
483483
case 1: { throw new Error('Not implemented yet: 1 case') }
484484
case 2: { throw new Error('Not implemented yet: 2 case') }
485485
}
486+
}
487+
`.trimRight(),
488+
},
489+
],
490+
},
491+
],
492+
},
493+
{
494+
// keys include special characters
495+
code: `
496+
export enum Enum {
497+
'test-test' = 'test-test',
498+
'test' = 'test',
499+
}
500+
501+
function test(arg: Enum): string {
502+
switch (arg) {
503+
}
504+
}
505+
`.trimRight(),
506+
errors: [
507+
{
508+
messageId: 'switchIsNotExhaustive',
509+
suggestions: [
510+
{
511+
messageId: 'addMissingCases',
512+
output: noFormat`
513+
export enum Enum {
514+
'test-test' = 'test-test',
515+
'test' = 'test',
516+
}
517+
518+
function test(arg: Enum): string {
519+
switch (arg) {
520+
case Enum['test-test']: { throw new Error('Not implemented yet: Enum['test-test'] case') }
521+
case Enum.test: { throw new Error('Not implemented yet: Enum.test case') }
522+
}
523+
}
524+
`.trimRight(),
525+
},
526+
],
527+
},
528+
],
529+
},
530+
{
531+
// keys include empty string
532+
code: `
533+
export enum Enum {
534+
'' = 'test-test',
535+
'test' = 'test',
536+
}
537+
538+
function test(arg: Enum): string {
539+
switch (arg) {
540+
}
541+
}
542+
`.trimRight(),
543+
errors: [
544+
{
545+
messageId: 'switchIsNotExhaustive',
546+
suggestions: [
547+
{
548+
messageId: 'addMissingCases',
549+
output: noFormat`
550+
export enum Enum {
551+
'' = 'test-test',
552+
'test' = 'test',
553+
}
554+
555+
function test(arg: Enum): string {
556+
switch (arg) {
557+
case Enum['']: { throw new Error('Not implemented yet: Enum[''] case') }
558+
case Enum.test: { throw new Error('Not implemented yet: Enum.test case') }
559+
}
560+
}
561+
`.trimRight(),
562+
},
563+
],
564+
},
565+
],
566+
},
567+
{
568+
// keys include number as first character
569+
code: `
570+
export enum Enum {
571+
'9test' = 'test-test',
572+
'test' = 'test',
573+
}
574+
575+
function test(arg: Enum): string {
576+
switch (arg) {
577+
}
578+
}
579+
`.trimRight(),
580+
errors: [
581+
{
582+
messageId: 'switchIsNotExhaustive',
583+
suggestions: [
584+
{
585+
messageId: 'addMissingCases',
586+
output: noFormat`
587+
export enum Enum {
588+
'9test' = 'test-test',
589+
'test' = 'test',
590+
}
591+
592+
function test(arg: Enum): string {
593+
switch (arg) {
594+
case Enum['9test']: { throw new Error('Not implemented yet: Enum['9test'] case') }
595+
case Enum.test: { throw new Error('Not implemented yet: Enum.test case') }
596+
}
486597
}
487598
`.trimRight(),
488599
},

0 commit comments

Comments
 (0)
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