
eslint-plugin-unicorn 的 no-useless-switch-case 规则彻底清理 switch 语句中的无用空 case【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn导读no-useless-switch-case是 eslint-plugin-unicorn 中用于消除switch语句里无意义空 case的代码质量规则。它专门揪出位于default分支之前、既没有语句体又与后续分支行为完全相同的空 case避免代码误导读者以为该 case 存在独立行为。本文将以该规则为主线结合其源码实现、单元测试与配置体系讲解它的触发条件、TypeScript 特例、修复方式及适用边界读完即可在真实项目中正确启用并理解其每次报错的依据。一、规则定位一条建议型suggestion规则no-useless-switch-case的官方描述是 Disallow useless case in switch statements禁止 switch 语句中出现无用的 case。在 readme.md 的规则索引表中它被标记为✅ 已纳入recommended推荐配置☑️ 已纳入unopinionated非主观配置 属于可通过编辑器建议手动修复的规则。也就是说它默认随plugin:unicorn/recommended开启几乎所有使用 unicorn 推荐配置的项目都会直接受益无需额外手动开启。从规则元信息看rules/no-useless-switch-case.jsmeta.type为suggestion属于代码风格/质量类建议meta.docs.recommended为unopinionatedmeta.hasSuggestions为true支持编辑器快速修复meta.languages限定为[js/js]即该规则当前仅作用于 JavaScript 文件对 TypeScript 文件同样适用见下文 TS 特例。二、规则核心空 case 位于最后的 default 之前即为无用规则文档给出的判定非常明确位于最后一个defaultcase 之前的空 caseempty case是无用的。删除它可以避免代码暗示该 case 具有独立行为——因为当执行流落入该 case 时会立即进入default分支两者行为完全相同。以最典型的违规代码为例// ❌ 报错case 1 是空的且紧跟在最后的 default 之前 switch (foo) { case 1: default: handleDefaultCase(); break; }这里的case 1:没有语句体无论foo是否为 1最终都会执行handleDefaultCase()。因此case 1:不传达任何额外语义属于纯冗余// ✅ 直接删除空 case 即可 switch (foo) { default: handleDefaultCase(); break; }判定边界只检查最后一个 case 是 default的情形需要特别注意的是规则只检查最后一个 case 是default的 switch 语句。这一点在原文档中明确说明也与源码逻辑一一对应rules/no-useless-switch-case.js// 仅当 case 数量 2且最后一个 case 的 test 为 null即 default 分支时才进入检查 if (cases.length 2 || cases.at(-1).test ! null) { return; }因此以下写法都不会触发报错// ✅ 空的 case 1、case 2 作为多值共享同一语句体的写法完全合法 switch (foo) { case 1: case 2: handleCase1And2(); break; }// ✅ 最后一个 case 不是 default不在检查范围内 switch (foo) { // This is actually useless, but we only check cases where the last case is the default case case 1: default: handleDefaultCase(); break; case 2: handleCase2(); break; }// ✅ 空 case 之前存在非空 case显式 fallthrough不报错 switch (foo) { case 1: handleCase1(); // Fallthrough default: handleDefaultCase(); break; }// ✅ case 有自己的语句体不报错 switch (foo) { case 1: handleCase1(); break; default: handleDefaultCase(); break; }如何判断空递归的空节点判定规则对空 case的判定并非只看是否没有任何字符而是通过工具函数isEmptySwitchCaserules/no-useless-switch-case.jsconst isEmptySwitchCase node node.consequent.every(node isEmptyNode(node));其底层调用的isEmptyNoderules/ast/is-empty-node.js会递归判断BlockStatement块内的每一条语句都递归判空EmptyStatement即裸分号;视为空其他节点默认为非空。这意味着下面这类看似写了点东西的 case 依然会被判定为空并报错对应测试用例// ❌ 嵌套空块与空语句本质仍是空 case switch (foo) { case a: { ;; { ;; { ;; } } } default: handleDefaultCase(); break; }此外测试中还覆盖了带注释、带括号 test 等变体test/no-useless-switch-case.js例如// ❌ 带括号的 test 表达式同样会被识别 switch (foo) { case a: case (( b )) : default: handleDefaultCase(); break; } // ❌ 即使加了 eslint-disable-next-line 注释规则依然报错disable 只抑制报错本身不影响规则判定 switch (foo) { // eslint-disable-next-line case a: case b: default: handleDefaultCase(); break; }三、TypeScript 特例允许case null:与case undefined:这是本规则最具设计巧思的地方。原文档明确指出在 TypeScript 文件中default:之前的空case null:和case undefined:是被允许的。原因是类型感知的穷尽性检查type-aware exhaustiveness check可能要求显式处理 union 类型中的null/undefined成员同时default:仍然负责兜底意外值。若强行删除这些空 case会导致类型检查层面的穷尽性约束被破坏。因此规则在 TypeScript 上下文中对这两种 case 网开一面。源码中的实现证据在 rules/no-useless-switch-case.js 中可以看到完整的判定逻辑const isNullishSwitchCase node isUndefined(node.test) || isNullLiteral(node.test); // 在 create 中 const isTypeScript isTypeScriptFile(context.physicalFilename); // ... for (let index cases.length - 2; index 0; index--) { const node cases[index]; if (!isEmptySwitchCase(node)) { break; } // TypeScript 文件中的空 case null / case undefined 放行 if (isTypeScript isNullishSwitchCase(node)) { continue; } // 其余空 case 报错并给出删除建议 yield { /* ... */ }; }其中两个判定工具函数分别是isUndefinedrules/ast/is-undefined.jsnode?.type Identifier node.name undefined即字面上的undefined标识符isNullLiteralrules/ast/literal.jsnode?.type Literal node.raw null即字面上的null字面量。而文件是否为 TypeScript 文件则依据context.physicalFilename实际物理文件名而非--filename传入的逻辑名判断。对应的测试矩阵test/no-useless-switch-case.js 中的用例完整刻画了这条规则的 TypeScript 行为代码形态文件类型结果case undefined:default:.ts✅ 允许case null:default:.ts✅ 允许case null:case undefined:default:.ts✅ 允许case a:case undefined:default:.ts❌ 报错case a非 nullishcase undefined:default:.js即使配 TS parser❌ 报错非 TS 文件无豁免case a:default:.ts❌ 报错case a非 nullishcase undefined:/case null:default:普通 JS❌ 报错值得注意的是最后两行的对比isTypeScript判断依据的是物理文件名扩展名而非解析器。测试中有filename: file.js搭配parsers.typescript的用例结果依然报错——说明是否豁免完全取决于文件是否为.ts与是否使用 TS parser 无关。四、修复方式编辑器建议suggestion而非自动修复no-useless-switch-case不是一条 autofix 规则即eslint --fix不会自动修改代码它通过editor suggestions编辑器建议提供手动修复。在 rules/no-useless-switch-case.js 中规则对每个违规的 case 节点产出两条消息并附带修复建议yield { node, loc: getSwitchCaseHeadLocation(node, context), messageId: MESSAGE_ID_ERROR, // Useless case in switch statement. suggest: [ { messageId: MESSAGE_ID_SUGGESTION, // Remove this case. fix: fixer fixer.remove(node), // 修复动作整体删除该 case }, ], };其中loc由工具函数getSwitchCaseHeadLocationrules/utils/get-switch-case-head-location.js计算从 case 起始位置若存在 test 则从 test 开始到冒号colon token结束为止从而精确定位case 头的报错范围包括带括号 test 的变体const startToken node.test || sourceCode.getFirstToken(node); const colonToken sourceCode.getTokenAfter(startToken, isColonToken); return {start: sourceCode.getLoc(node).start, end: sourceCode.getLoc(colonToken).end};因此在 VS Code、WebStorm 等支持 ESLint 建议的编辑器中把光标移到报错处选择 Remove this case或等价的中文提示即可一键删除多余的空 case全程无需手动编辑。五、启用方式与适用建议通过 preset 开启该规则默认随以下配置启用见规则文档头部声明// 方式一flat config推荐 import eslintPluginUnicorn from eslint-plugin-unicorn; export default [ // ... eslintPluginUnicorn.configs[flat/recommended], // 或 eslintPluginUnicorn.configs[flat/unopinionated] ];// 方式二eslintrc 风格 { extends: [plugin:unicorn/recommended] }手动开启自定义配置时// eslint.config.js export default [ { files: [**/*.js, **/*.ts], plugins: {unicorn: eslintPluginUnicorn}, rules: { unicorn/no-useless-switch-case: error, }, }, ];什么时候适合关闭或放宽从规则的判定逻辑可以反推它的适用边界不检查最后一个 case 非 default的 switch——如果你的代码刻意依赖空 case 落入 default 之后再落到下一个具名 case的穿透行为如测试用例switch (1) { case 1: default: ... case 1: ... }规则不会误伤TypeScript 的穷尽性检查场景——case null/case undefined空 case 默认豁免无需手动 disable显式 fallthrough 语义——空 case 之前若已有带语句体的 case规则会立即break停止向前回溯源码中的if (!isEmptySwitchCase(node)) break;不会把正常的穿透链误判为冗余。若你的代码库确有特殊需求可按需调整rules: { unicorn/no-useless-switch-case: [error, /* 该规则当前无额外选项 */], // 或整体关闭 // unicorn/no-useless-switch-case: off, }六、总结no-useless-switch-case的判定逻辑可以浓缩为一句话在一个以 default 收尾的 switch 中从尾部倒数、紧贴 default 且完全为空含递归空块/空语句的非 nullish case都是无用代码。它的价值在于防止空 case default这种写法误导读者——让每个 case 都承载真实、可区分的语义。若想深入了解其实现细节推荐按以下顺序阅读仓库源码规则主体rules/no-useless-switch-case.js判定循环、TypeScript 豁免、suggestion 修复空节点判定rules/ast/is-empty-node.jsnullish 判定rules/ast/is-undefined.js、rules/ast/literal.js报错位置计算rules/utils/get-switch-case-head-location.js覆盖全部边界情况的测试test/no-useless-switch-case.js配合recommended配置与编辑器的 Remove this case 建议你可以低成本地让代码库中的switch语句更干净、更诚实。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考