ESLint eqeqeq 规则完全指南:强制使用 === 与 !== 消除隐式类型转换陷阱
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
本指南围绕 ESLint 内置规则eqeqeq展开,讲解其为何被广泛推荐、always/smart/allow-null三种模式及null子选项的完整配置方式,并结合仓库源码 lib/rules/eqeqeq.js 与测试用例 tests/lib/rules/eqeqeq.js 剖析其自动修复(--fix)与建议修复(suggestion)的底层判定逻辑。读完你将能精确配置该规则,并理解其在什么情况下可以安全自动修复、什么情况下只提供建议。
为什么推荐使用===与!==
业界普遍将类型严格相等运算符===与!==视为良好实践,而非类型安全的==与!=被认为应当避免。根本原因在于:==与!=会执行隐式类型转换(type coercion),其行为遵循 JavaScript 规范中晦涩难懂的抽象相等比较算法(Abstract Equality Comparison Algorithm),极易引发难以察觉的 Bug。
例如,下面这些表达式在==下全部判定为true:
[] == false[] == ![]3 == "03"
如果其中一个出现在看似人畜无害的语句a == b中,实际的问题往往极难定位——这正是eqeqeq规则存在的意义:在编译(检查)阶段就把类型不安全比较拦截下来。
Rule Details:规则如何识别问题
eqeqeq规则的目标是消除类型不安全的相等运算符。它监听 AST 中的BinaryExpression节点(见 lib/rules/eqeqeq.js),当节点的operator为==或!=时触发报告。
该规则的错误(incorrect)代码示例:
/*eslint eqeqeq: "error"*/ if (x == 42) { } if ("" == text) { } if (obj.getStuff() != undefined) { }规则在元信息中声明为type: "suggestion"、hasSuggestions: true且fixable: "code"(见 lib/rules/eqeqeq.js),这意味着它既支持命令行自动修复,也支持编辑器中"快速修复"式的建议。该规则默认不在推荐配置eslint:recommended中(recommended: false),需要显式开启。
自动修复的边界:何时--fix,何时只给建议
命令行--fix选项会自动修复该规则报告的一部分问题,判定条件在 lib/rules/eqeqeq.js 的report函数中:
- 满足以下任一条件时直接自动修复:
- 其中一个操作数是
typeof表达式; - 两个操作数都是同类型的字面量。
- 其中一个操作数是
- 其余情况只提供 suggestion 建议(可手动应用,或经编辑器触发),因为此时修改运算符可能改变运行时行为——当两个操作数类型不同时,
==与===的语义并不等价。
从源码看,"同类型字面量"的判定由getLiteralType与areLiteralsAndSameType完成(lib/rules/eqeqeq.js):
- 普通
Literal节点按其值的typeof结果归类; - 无插值的静态模板字符串(如
`hello`,即TemplateLiteral且expressions.length === 0)归为"string"类型; - 其他情况返回
null,即"无法确认类型",此时不会自动修复,只提供建议。
规则会向用户报告两类消息(messages定义于 lib/rules/eqeqeq.js):
unexpected:Expected '{{expectedOperator}}' and instead saw '{{actualOperator}}'.replaceOperator(建议消息):Use '{{expectedOperator}}' instead of '{{actualOperator}}'.
Options 总览
eqeqeq的配置 schema(见 lib/rules/eqeqeq.js)接受两种形态:
["error", "always", { "null": "..." }]——always可带可选的第二参数对象;["error", "smart"]或["error", "allow-null"]——单独字符串选项。
默认选项为["always"](defaultOptions: ["always"])。
always(默认)
"always"选项(默认值)要求所有场景都使用===与!==(除非你通过下面的"null"子选项对null做了特化处理)。
"always"选项下的错误(incorrect)代码:
/*eslint eqeqeq: ["error", "always"]*/ a == b foo == true bananas != 1 value == undefined typeof foo == 'undefined' 'hello' != 'world' 0 == 0 true == true foo == null"always"选项下的正确(correct)代码:
/*eslint eqeqeq: ["error", "always"]*/ a === b foo === true bananas !== 1 value === undefined typeof foo === 'undefined' 'hello' !== 'world' 0 === 0 true === true foo === null该选项可选地接收第二个参数(对象),支持的属性如下:
| 属性 | 可选值 | 说明 |
|---|---|---|
null | always(默认) | 始终要求对null使用===或!== |
null | never | 永远不对null使用===或!== |
null | ignore | 不对null应用本规则 |
例如:
/*eslint eqeqeq: ["error", "always", {"null": "ignore"}]*/ foo == null // 正确:与 null 的比较被放行 foo === null // 正确:严格比较同样被允许 a == b // 错误:变量间比较仍必须严格smart
"smart"选项强制使用===与!==,但排除以下三种情况:
- 比较两个字面量;
- 对
typeof的结果求值; - 与
null比较。
"smart"选项下的错误(incorrect)代码:
/*eslint eqeqeq: ["error", "smart"]*/ // 比较两个变量必须使用 === a == b // 只有一侧是字面量 foo == true bananas != 1 // 与 undefined 比较必须使用 === value == undefined"smart"选项下的正确(correct)代码:
/*eslint eqeqeq: ["error", "smart"]*/ typeof foo == 'undefined' 'hello' != 'world' 0 == 0 true == true foo == null从源码看,smart模式的放行逻辑在 lib/rules/eqeqeq.js:当配置为"smart"且满足"任一侧是typeof表达式、或两侧是同类型字面量、或任一侧是null字面量"之一时直接return,不报告。注意true == 1这类不同类型的字面量比较,在smart下依然会被报告(参见 tests/lib/rules/eqeqeq.js),因为二者类型不同、==语义不可靠。
allow-null(已弃用)
已弃用(Deprecated):请改用"always"并配合"null"子选项值"ignore",它告诉 ESLint:除与null字面量比较外,一律强制严格相等。
["error", "always", {"null": "ignore"}]尽管已弃用,allow-null在 schema 中仍被保留并兼容解析,等价于上述配置。
null 子选项的源码级解读
"null"子选项的生效逻辑位于 lib/rules/eqeqeq.js:
const nullOption = config === "always" ? options.null || "always" : "ignore"; const enforceRuleForNull = nullOption === "always"; const enforceInverseRuleForNull = nullOption === "never";值得注意的两点:
null子选项只在"always"模式下生效;若配合"smart"或"allow-null",其值被强制视为"ignore"。- 当
"null": "never"时,规则逻辑发生反转:不仅放行a == null,还会反向报告a === null/a !== null,并建议改为==/!=。对应报告逻辑在 lib/rules/eqeqeq.js(对===/!==且含null字面量的节点,将运算符去除一个=后报告),测试用例见 tests/lib/rules/eqeqeq.js。
如何识别"真正的 null 字面量"
规则判断是否与null比较时,调用了 lib/rules/utils/ast-utils.js 中的isNullLiteral。该函数要求节点是Literal、value === null、且不是正则字面量(node.regex)也不是 BigInt 字面量(node.bigint)。注释解释了原因:某些环境下无法表示的值(如旧版 Node 中的 Unicode 正则)解析后node.value也会是null,仅凭value === null会误判(见 eslint issue #8020)。这也是测试中专门覆盖foo === /abc/u与foo === 1n的原因(tests/lib/rules/eqeqeq.js)。
修复行为实测:哪些代码会被直接改写
通过测试用例可以精确归纳自动修复的覆盖范围(tests/lib/rules/eqeqeq.js):
| 输入代码 | 自动修复结果 | 依据 |
|---|---|---|
typeof a == 'number' | typeof a === 'number'(直接修复) | typeof 操作数命中自动修复条件 |
true == true | true === true(直接修复) | 两侧同为 boolean 字面量 |
2 == 3 | 2 === 3(直接修复) | 两侧同为 number 字面量 |
`hello` == `world` | `hello` === `world`(直接修复) | 静态模板字符串均视为 string |
a == b | 仅建议a === b | 两侧是变量,无法确认类型 |
a == null(默认) | 仅建议a === null | 变量与 null,未满足自动修复条件 |
(a) == b | 仅建议(a) === b(括号保留) | 建议修复只替换运算符 token |
a\n==\nb | a\n===\nb(直接修复) | 运算符 token 定位准确,可跨行 |
关键实现细节:报告的定位使用sourceCode.getFirstTokenBetween(node.left, node.right, ...)精确找到运算符 token(lib/rules/eqeqeq.js),因此建议/修复只会替换运算符本身,不会破坏左右表达式原有的括号、换行与空白,如测试中(a + b) != c;→(a + b) !== c;(tests/lib/rules/eqeqeq.js)。嵌套比较(a == b) == (c)会分别报告两处==,见 tests/lib/rules/eqeqeq.js。
在配置文件中启用
eqeqeq通过 lib/rules/index.js 注册(eqeqeq: () => require("./eqeqeq")),属于 ESLint 内置规则,直接配置即可:
{ "rules": { "eqeqeq": ["error", "always", { "null": "ignore" }] } }针对只希望"变量间比较必须严格、同时允许 null 判断"的团队,推荐组合"always"+{"null": "ignore"};若希望保留typeof、同类型字面量与null比较的宽松写法,则选择"smart"。两者均可在eslint --fix或编辑器保存时自动应用安全的那部分修复。
何时不使用该规则
如果你不想对相等运算符的书写风格做任何强制,那么直接关闭本规则是安全的:
{ "rules": { "eqeqeq": "off" } }不过需要注意:关闭它意味着代码中==的隐式类型转换风险将完全交由开发者自律,建议在彻底理解抽象相等比较算法语义的团队中再作此决定。
延伸阅读
- 规则源码:lib/rules/eqeqeq.js
- 测试用例(覆盖 929 行断言,含 null 子选项、smart 模式、自动修复与建议输出):tests/lib/rules/eqeqeq.js
- 辅助工具函数(
isNullLiteral、isStaticTemplateLiteral):lib/rules/utils/ast-utils.js - 规则注册入口:lib/rules/index.js
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考