From a568bca4dc548c06a92424f155e5d66e00624761 Mon Sep 17 00:00:00 2001 From: Hendrik Liebau Date: Mon, 1 Jun 2026 20:55:36 +0200 Subject: [PATCH] Disable `default-case` ESLint rule for type-checked TypeScript files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `@typescript-eslint/switch-exhaustiveness-check` (enabled in eslint.cli.config.mjs) already guarantees complete switch coverage on enums and discriminated unions, so requiring an additional default case on those switches in TypeScript just forced dead branches. We turn on its `requireDefaultForNonUnion` option so that switches on plain types such as `string` or `number` still require a default, preserving the coverage that `default-case` previously gave us. We scope the `default-case: off` override to exactly the files where the type-checked rule runs — the same `files` and `ignores` as the switch-exhaustiveness check — and cross-reference the two config blocks so the globs stay in sync. That way `default-case` stays enabled for JavaScript files, `.mts` files, and the directories the type-checked config skips, so no switch is left unchecked. The two `// eslint-disable-next-line default-case` directives in `postcss-loader` and `head.tsx` are no longer the right escape hatch: with `requireDefaultForNonUnion` the exhaustiveness check now wants those switches handled, and a directive for that rule would be reported as unused under the editor config where it does not run. We give each switch an explicit `default` case instead, which both rules accept and which makes the previously implicit fall-through behavior explicit. --- eslint.cli.config.mjs | 8 ++++++- eslint.config.mjs | 24 +++++++++++++++++++ .../loaders/postcss-loader/src/index.ts | 4 +++- packages/next/src/shared/lib/head.tsx | 3 ++- 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/eslint.cli.config.mjs b/eslint.cli.config.mjs index 85729ea54796..32b7d7529ec1 100644 --- a/eslint.cli.config.mjs +++ b/eslint.cli.config.mjs @@ -10,6 +10,9 @@ export default defineConfig([ // This override adds type-checked rules. // Linting with type-checked rules is very slow and needs a lot of memory, // so we exclude non-essential files. + // NOTE: eslint.config.mjs has a config block that mirrors these + // `files`/`ignores` to override non-type-checked rules for the same set of + // files. Keep both in sync if you change the globs. ignores: [ 'bench/**/*', 'examples/**/*', @@ -27,7 +30,10 @@ export default defineConfig([ rules: { // TODO: enable in follow-up PR '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/switch-exhaustiveness-check': 'error', + '@typescript-eslint/switch-exhaustiveness-check': [ + 'error', + { requireDefaultForNonUnion: true }, + ], }, }, ]) diff --git a/eslint.config.mjs b/eslint.config.mjs index 4a0f2ca7b9e4..3ff56f0429d2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -394,6 +394,30 @@ export default defineConfig([ '@typescript-eslint/prefer-literal-enum-member': 'error', }, }, + { + // This block mirrors the `files`/`ignores` of the type-checked config in + // eslint.cli.config.mjs, so it targets exactly the files for which those + // type-aware rules run. Use it to override non-type-checked rules whose + // behavior overlaps with a type-checked rule. Keep the globs below in sync + // with eslint.cli.config.mjs. + files: ['**/*.ts', '**/*.tsx'], + ignores: [ + 'bench/**/*', + 'examples/**/*', + 'test/**/*', + '**/*.d.ts', + 'turbopack/**/*', + ], + rules: { + // `@typescript-eslint/switch-exhaustiveness-check` already enforces + // complete switch coverage on these files: every member of a union or + // enum must be handled, and with `requireDefaultForNonUnion` a default + // is required for switches on plain types (string, number, …). Leaving + // `default-case` on would additionally force a redundant default on + // exhaustive union/enum switches. + 'default-case': 'off', + }, + }, { files: ['packages/**/*.ts', 'packages/**/*.tsx'], plugins: { diff --git a/packages/next/src/build/webpack/loaders/postcss-loader/src/index.ts b/packages/next/src/build/webpack/loaders/postcss-loader/src/index.ts index 834efcbac12f..2fca8a06bd9c 100644 --- a/packages/next/src/build/webpack/loaders/postcss-loader/src/index.ts +++ b/packages/next/src/build/webpack/loaders/postcss-loader/src/index.ts @@ -83,7 +83,6 @@ export default async function loader( } for (const message of result.messages) { - // eslint-disable-next-line default-case switch (message.type) { case 'dependency': this.addDependency(message.file) @@ -109,6 +108,9 @@ export default async function loader( message.info ) } + break + default: + break } } diff --git a/packages/next/src/shared/lib/head.tsx b/packages/next/src/shared/lib/head.tsx index 74e91b6fdfe0..f40fa3c6d215 100644 --- a/packages/next/src/shared/lib/head.tsx +++ b/packages/next/src/shared/lib/head.tsx @@ -73,7 +73,6 @@ function unique() { } } - // eslint-disable-next-line default-case switch (h.type) { case 'title': case 'base': @@ -106,6 +105,8 @@ function unique() { } } break + default: + break } return isUnique