From 141eb5900ad8028f4284ec2cb1ce8d0334d80185 Mon Sep 17 00:00:00 2001 From: Ronen Amiel Date: Tue, 29 Oct 2024 22:09:19 +0200 Subject: [PATCH 1/5] don't report on the 'missing' undefined type if it's covered with an undefined type --- .../src/rules/switch-exhaustiveness-check.ts | 9 +++ .../rules/switch-exhaustiveness-check.test.ts | 55 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts index 62fbfcbd3c6f..27e8deff7556 100644 --- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts +++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts @@ -155,6 +155,15 @@ export default createRule({ continue; } + // `undefined` should cover the "missing" undefined type + // https://github.com/microsoft/TypeScript/blob/cb44488fcec4348a448434afbf2ebcbf2b423c61/src/compiler/checker.ts/#L2059 + if ( + caseTypes.has(checker.getUndefinedType()) && + tsutils.isIntrinsicUndefinedType(intersectionPart) + ) { + continue; + } + missingLiteralBranchTypes.push(intersectionPart); } } 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 a3e9dd90cdd0..0490d8c4163e 100644 --- a/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts +++ b/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts @@ -912,6 +912,24 @@ switch (value) { }, ], }, + { + code: ` +function foo(x: string[]) { + switch (x[0]) { + case 'hi': + break; + case undefined: + break; + } +} + `, + languageOptions: { + parserOptions: { + project: './tsconfig.noUncheckedIndexedAccess.json', + tsconfigRootDir: rootPath, + }, + }, + }, ], invalid: [ { @@ -2717,5 +2735,42 @@ switch (value) { }, ], }, + { + code: ` +function foo(x: string[]) { + switch (x[0]) { + case 'hi': + break; + } +} + `, + errors: [ + { + column: 11, + line: 3, + messageId: 'switchIsNotExhaustive', + suggestions: [ + { + messageId: 'addMissingCases', + output: ` +function foo(x: string[]) { + switch (x[0]) { + case 'hi': + break; + case undefined: { throw new Error('Not implemented yet: undefined case') } + } +} + `, + }, + ], + }, + ], + languageOptions: { + parserOptions: { + project: './tsconfig.noUncheckedIndexedAccess.json', + tsconfigRootDir: rootPath, + }, + }, + }, ], }); From f7ab3c4726fb7babbbdd491697335b1032c01115 Mon Sep 17 00:00:00 2001 From: Ronen Amiel Date: Tue, 29 Oct 2024 22:56:43 +0200 Subject: [PATCH 2/5] projectService: false --- .../tests/rules/switch-exhaustiveness-check.test.ts | 1 + 1 file changed, 1 insertion(+) 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 0490d8c4163e..40db39465380 100644 --- a/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts +++ b/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts @@ -2768,6 +2768,7 @@ function foo(x: string[]) { languageOptions: { parserOptions: { project: './tsconfig.noUncheckedIndexedAccess.json', + projectService: false, tsconfigRootDir: rootPath, }, }, From e52e44cab17e921aaee417640770046ad21aaa55 Mon Sep 17 00:00:00 2001 From: Ronen Amiel Date: Tue, 29 Oct 2024 22:58:08 +0200 Subject: [PATCH 3/5] projectService: false on valid test too --- .../tests/rules/switch-exhaustiveness-check.test.ts | 1 + 1 file changed, 1 insertion(+) 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 40db39465380..8efe52223363 100644 --- a/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts +++ b/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts @@ -926,6 +926,7 @@ function foo(x: string[]) { languageOptions: { parserOptions: { project: './tsconfig.noUncheckedIndexedAccess.json', + projectService: false, tsconfigRootDir: rootPath, }, }, From 674c1589d17fbd98055f1a8765d7093f087202ae Mon Sep 17 00:00:00 2001 From: Ronen Amiel Date: Wed, 30 Oct 2024 18:57:51 +0200 Subject: [PATCH 4/5] Update packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts Co-authored-by: auvred <61150013+auvred@users.noreply.github.com> --- .../eslint-plugin/src/rules/switch-exhaustiveness-check.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts index 27e8deff7556..8dd9c0f26b27 100644 --- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts +++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts @@ -155,10 +155,10 @@ export default createRule({ continue; } - // `undefined` should cover the "missing" undefined type - // https://github.com/microsoft/TypeScript/blob/cb44488fcec4348a448434afbf2ebcbf2b423c61/src/compiler/checker.ts/#L2059 + // "missing", "optional" and "undefined" types are different runtime objects, + // but all of them have TypeFlags.Undefined type flag if ( - caseTypes.has(checker.getUndefinedType()) && + Array.from(caseTypes).some(tsutils.isIntrinsicUndefinedType) && tsutils.isIntrinsicUndefinedType(intersectionPart) ) { continue; From 64a712c01cb6eb5cba809a9a8422b91692aad85d Mon Sep 17 00:00:00 2001 From: Ronen Amiel Date: Wed, 30 Oct 2024 19:01:10 +0200 Subject: [PATCH 5/5] add relevant test --- .../src/rules/switch-exhaustiveness-check.ts | 2 +- .../rules/switch-exhaustiveness-check.test.ts | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts index 8dd9c0f26b27..4b5b178068cd 100644 --- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts +++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts @@ -158,7 +158,7 @@ export default createRule({ // "missing", "optional" and "undefined" types are different runtime objects, // but all of them have TypeFlags.Undefined type flag if ( - Array.from(caseTypes).some(tsutils.isIntrinsicUndefinedType) && + [...caseTypes].some(tsutils.isIntrinsicUndefinedType) && tsutils.isIntrinsicUndefinedType(intersectionPart) ) { continue; 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 8efe52223363..c7b9b62e9ea4 100644 --- a/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts +++ b/packages/eslint-plugin/tests/rules/switch-exhaustiveness-check.test.ts @@ -921,6 +921,29 @@ function foo(x: string[]) { case undefined: break; } +} + `, + languageOptions: { + parserOptions: { + project: './tsconfig.noUncheckedIndexedAccess.json', + projectService: false, + tsconfigRootDir: rootPath, + }, + }, + }, + { + code: ` +function foo(x: string[], y: string | undefined) { + const a = x[0]; + if (typeof a === 'string') { + return; + } + switch (y) { + case 'hi': + break; + case a: + break; + } } `, languageOptions: {