ESLint default-case 规则详解:强制 switch 语句声明 default 分支
2026/9/12 4:03:28 网站建设 项目流程

ESLint default-case 规则详解:强制 switch 语句声明 default 分支

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

default-case是 ESLint 内置的一条suggestion(建议类)规则,核心职责是强制要求代码中所有switch语句都必须包含default分支,以此显式表达"未命中任何 case 时应该怎么做",避免开发者因遗忘而留下未定义行为。本文以本仓库(eslint)中的规则文档 docs/src/rules/default-case.md 为主体,结合 lib/rules/default-case.js 的源码实现与 tests/lib/rules/default-case.js 的测试用例,系统讲解该规则的适用场景、判断逻辑、commentPattern配置与禁用时机,帮助你在实际项目中正确启用与使用它。

规则动机:为什么要强制 default 分支

在 JavaScript 中,switch语句的case分支并不是穷举式的——当表达式的值不匹配任何case时,程序会跳过整个switch体而不执行任何逻辑。如果开发者的本意是"未匹配时什么都不做",这种写法是合理的;但问题在于,没有default分支时,无法从代码上区分"有意省略"和"意外遗忘"

部分团队的代码规范要求所有switch语句都必须有default分支,即使该分支是空实现,也要显式写出来:

switch (foo) { case 1: doSomething(); break; case 2: doSomething(); break; default: // do nothing }

其背后的理念是:始终显式声明默认行为应该是什么,这样代码阅读者(包括未来的自己)能清楚地确认开发者不是"忘了写"默认处理,而是"有意不处理"。

另一部分团队则允许省略default,但前提是必须用注释说明省略是有意为之,例如:

switch (foo) { case 1: doSomething(); break; case 2: doSomething(); break; // no default }

同样,这里的意图也是向读者传达:开发者明确希望没有默认行为

Rule Details:规则如何判定

default-case规则的目标就是要求switch语句具备default分支。在省略default的情况下,你可以在最后一个case之后添加一条// no default注释来声明"有意省略"。该注释不区分大小写,例如// No Default// NO DEFAULT均被接受。

该规则在仓库中的元信息如下(见 lib/rules/default-case.js):

  • type: "suggestion":属于建议类规则,不直接指向运行时错误;
  • recommended: false未收录在eslint:recommended推荐配置中,需要手动开启;
  • messages.missingDefaultCase: "Expected a default case.":未命中时的报错文案;
  • defaultOptions: [{}]:默认选项为空对象,即不传参也可运行。

错误的代码示例

以下写法没有default分支、也没有豁免注释,会被判定为违规:

/*eslint default-case: "error"*/ switch (a) { case 1: /* code */ break; }

正确的代码示例

以下三种写法均合规:显式default分支、// no default小写注释、// No Default任意大小写注释:

/*eslint default-case: "error"*/ switch (a) { case 1: /* code */ break; default: /* code */ break; } switch (a) { case 1: /* code */ break; // no default } switch (a) { case 1: /* code */ break; // No Default }

Options:配置项说明

该规则接受一个可选的对象参数,其中只有一个属性:

  • commentPattern:一个正则表达式字符串,用于替换默认的注释匹配模式/^no default$/i。当你的团队习惯使用其他措辞(如// skip default)来表示"有意省略"时,可自定义该模式。

commentPattern 用法示例

以下配置将豁免注释模式改为^skip\sdefault,即注释以skip default开头即可视为有效声明:

/*eslint default-case: ["error", { "commentPattern": "^skip\\sdefault" }]*/ switch(a) { case 1: /* code */ break; // skip default } switch(a) { case 1: /* code */ break; // skip default case }

注意:commentPattern字符串而非字面量正则,因此书写时需要在字符串内对反斜杠进行转义(如上例中的\\s)。规则内部会将其作为正则表达式源码构造。

源码级原理:规则的执行流程

要深入理解default-case的判定边界,需要结合其实现代码 lib/rules/default-case.js 来看,该规则的整体逻辑非常精炼,全部集中在create(context)中返回的SwitchStatement访问器里。

默认注释模式的定义

第 7 行定义了默认模式常量:

const DEFAULT_COMMENT_PATTERN = /^no default$/iu;

注意这里有两个标志:

  • i:大小写不敏感,所以No DefaultNO DEFAULTno dEFAUlt都能命中(测试用例 tests/lib/rules/default-case.js 覆盖了这些变体);
  • u:Unicode 模式,确保对多字节字符的正则语义正确。

如果配置了commentPattern,规则会用new RegExp(options.commentPattern, "u")构造新正则(见 lib/rules/default-case.js)——只保留u标志,不再自动附加i,因此自定义模式的大小写敏感性取决于你写的模式本身。

五个关键判定步骤

访问器SwitchStatement(node)(lib/rules/default-case.js)的执行流程可以拆解为:

  1. 空 switch 直接跳过:如果node.cases.length为 0,立即return,不报错。源码注释解释了原因:空switch内部无法便捷地提取注释,所以跳过检查(测试用例"switch (a) { }"为 valid)。注意switch (a) { // no default }这种带注释的空 switch 也是合法的;
  2. 检测是否含 default 分支:通过node.cases.some(v => v.test === null)判断——在 ESTree AST 中,default:分支的test属性为null,这是区分default与其他case的判定依据;
  3. 取最后一个 case 的后续注释:调用sourceCode.getCommentsAfter(lastCase)获取紧跟最后一个case节点之后的注释,然后取其中最后一条comment作为判定对象;
  4. 注释内容匹配:对comment.value.trim()(去除首尾空白)执行commentPattern.test()
  5. 判定与上报:如果没有注释、或注释内容不匹配模式,则context.report({ node, messageId: "missingDefaultCase" })报出Expected a default case.错误。

getCommentsAfter 的底层实现

sourceCode.getCommentsAfter由 token-store 提供,实现在 lib/languages/js/source-code/token-store/index.js:它利用向前游标(cursors.forward),从给定节点或 token 的range[1](结束位置)开始,收集直接相邻的注释 token 并以出现顺序返回。这意味着规则只关心紧跟在最后一个case后面的注释——隔了其他语句或代码的注释不会被误判为豁免声明。

边界行为:测试用例揭示的细节

测试文件 tests/lib/rules/default-case.js 用大量 valid/invalid 用例锁定了规则的边界行为:

  • 只取最后一条注释case 1: break; // no default \n // nope是 invalid——最后一条注释是// nope,不匹配默认模式,前面的// no default无效;
  • 注释必须紧跟在最后一个 case 之后// no default写在case 1之前switch (a) { // no default \n case 1: break; })是 invalid,因为getCommentsAfter取的是最后一个 case 之后的注释,位置不对就视为省略未声明;
  • 块注释同样有效/* no default */// no default效果一致(源码对注释类型不敏感,只取value);
  • 匹配的是 trim 后的内容:注释内容的首尾空白会被去除后再测试,因此// no default(带多余空格)也能通过;
  • 自定义模式不匹配即报错:配置commentPattern: "skipped default case"时,// no default反而会触发missingDefaultCase——自定义模式是完全替换默认模式,而非叠加。

与 no-fallthrough 的互补关系

规则文档 front matter 中声明了关联规则no-fallthrough(见 docs/src/rules/default-case.md)。default-case关注的是"是否有默认分支",而no-fallthrough(文档见 docs/src/rules/no-fallthrough.md)关注的是"case 之间是否发生非预期的贯穿(fallthrough)"。两者分别从分支完整性和分支终止性两个维度约束switch,在实际项目中常搭配使用:default-case强制声明默认行为,no-fallthrough强制每个 case 以breakreturnthrow或注释收尾。

When Not To Use It:何时关闭该规则

如果你不希望强制switch语句必须带default分支,可以放心地禁用此规则。典型的适用场景包括:

  • 团队代码风格接受省略default(例如某些 DSL 或状态机代码中,switch只用于穷举已知枚举值);
  • 项目中switch使用极其频繁,且大多数场景确实不需要默认行为,强制声明会带来大量噪音;
  • 已有其他约定(如代码评审规范)来保证default分支不被遗漏,无需在 lint 层面强制。

由于该规则未被eslint:recommended收录,禁用时只需在配置文件中将该规则设为"off"即可,不会影响其他规则。

在项目中使用 default-case

在扁平配置(flat config)中启用该规则并自定义豁免注释模式,可以参考如下写法(仓库自身的 lint 配置见 eslint.config.js):

export default [ { rules: { "default-case": ["error", { commentPattern: "^skip\\sdefault" }], }, }, ];

在传统.eslintrc配置中则等价于:

{ "rules": { "default-case": ["error", { "commentPattern": "^skip\\sdefault" }] } }

如果只需要最基本的强制检查,直接配置"default-case": "error"即可,此时豁免注释必须匹配默认模式/^no default$/iu。该规则在规则索引 lib/rules/index.js 中以懒加载方式注册,与仓库内其他 300 余条内置规则保持一致的加载机制。

总结

default-case是一条轻量但实用的建议类规则,通过强制switch语句声明default分支(或以// no default注释显式声明省略),让"默认行为"成为代码中的一等公民。其判定逻辑简单清晰:空 switch 跳过、有default放行、否则校验最后一个 case 后的最后一条注释是否匹配模式。理解它的五个判定步骤与getCommentsAfter的"直接相邻"语义,能帮你精准掌握豁免注释的合法位置,避免写出"看似豁免实则违规"的代码。

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询