尧图精选

eslint-plugin-unicorn 的 prefer-global-number-constants 规则深度解析:从快照测试看 NaN / Infinity 自动修复的完整行为

🕒 发布时间:2026/9/19 18:41:32 📁 来源:尧图网络
eslint-plugin-unicorn 的 prefer-global-number-constants 规则深度解析从快照测试看 NaN / Infinity 自动修复的完整行为【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicornprefer-global-number-constants是 eslint-plugin-unicorn 中一条将Number静态常量替换为全局数值常量的可自动修复规则。本文以仓库中的快照测试报告 test/snapshots/prefer-global-number-constants.js.md 为主体结合规则源码与测试用例逐条还原该规则在 9 个非法用例上的报错信息、自动修复输出与修复抑制边界并深入讲解其底层实现原理。读完本文你将掌握该规则的触发条件、三条替换映射、遮蔽检测、左手边过滤等核心机制以及它与prefer-number-properties规则的冲突关系可直接用于配置和排查实际 lint 结果。规则概览做什么、在哪启用、能否自动修复该规则的目标非常聚焦把冗长的Number静态常量写法替换为更短、更易读的全局数值常量。规则文档 docs/rules/prefer-global-number-constants.md 明确给出了三组替换关系写法不推荐替换为推荐Number.NaNNaNNumber.POSITIVE_INFINITYInfinityNumber.NEGATIVE_INFINITY-Infinity从规则的meta定义见 rules/prefer-global-number-constants.js可以看到type: suggestion属于代码风格建议类规则fixable: code支持eslint --fix自动修复recommended: unopinionated规则文档头部标明它被收录进recommended与unopinionated两套预设配置该规则只针对js/js语言languages: [js/js]不适用于 TypeScript 等其他语言。快照测试报告的第一段同样点明了它的身份这份报告由 AVA 测试框架自动生成Generated by AVA服务于test/prefer-global-number-constants.js这个测试文件真实的快照数据保存在同名的.snap文件中而.md版本是便于人工阅读的渲染结果。快照在测试体系中的角色一次所见即所得的回归保障在继续逐条分析用例之前有必要先理解这份快照报告的定位。eslint-plugin-unicorn 的规则测试通过 test/utils/test.js 的getTester封装了 ESLint 的RuleTester并对测试用例调用test.snapshot(...)。这意味着每个非法invalid用例的输入代码、报错消息、修复后的输出都会被完整记录下来快照文件.md与.snap由 AVA 自动生成任何对规则行为如消息文案、修复输出的改动都会导致快照不匹配从而在 CI 中失败——这是防止规则行为悄然回归的强力保障阅读快照报告等于直接看到规则在当前版本下的真实运行结果无需自行搭建环境。因此本文接下来的内容全部以快照中的真实输出为准绳并结合 rules/prefer-global-number-constants.js 的源码解释为什么会有这样的输出。九个非法用例逐条解析快照里的完整行为证据快照报告 test/snapshots/prefer-global-number-constants.js.md 记录了 9 个非法用例invalid(1)至invalid(9)。以下逐条还原其输入、报错与修复输出。1. 最基础的Number.NaNinvalid 1// 输入 const foo Number.NaN; // 报错 Prefer NaN over Number.NaN. // 修复输出 const foo NaN;错误高亮区间覆盖整个Number.NaN^^^^^^^^^^即问题节点是完整的内存取表达式MemberExpression。这是最简单的触发场景。2. 经window访问的window.Number.NaNinvalid 2const foo window.Number.NaN; // → Prefer NaN over Number.NaN. // 修复输出const foo NaN;注意报错消息依然写作 overNumber.NaN但输入写的是window.Number.NaN。这是因为规则底层使用的全局引用追踪器只关心成员访问链的后半段Number.NaNwindow.前缀会被忽略——window.Number与全局Number在浏览器环境中指向同一个对象。3. 计算成员访问Number[NaN]invalid 3const foo Number[NaN]; // → Prefer NaN over Number.NaN. // 修复输出const foo NaN;说明规则对计算属性computed member access同样敏感只要字符串键匹配NaN、POSITIVE_INFINITY、NEGATIVE_INFINITY之一即可命中。4. 正无穷Number.POSITIVE_INFINITYinvalid 4const foo Number.POSITIVE_INFINITY; // → Prefer Infinity over Number.POSITIVE_INFINITY. // 修复输出const foo Infinity;5. 负无穷Number.NEGATIVE_INFINITY只报错不修复invalid 5const foo Number.NEGATIVE_INFINITY; // → Prefer -Infinity over Number.NEGATIVE_INFINITY. // 无修复输出这是快照中最值得注意的用例它没有Output字段。也就是说Number.NEGATIVE_INFINITY会被报告但不会自动修复。原因在规则文档中写得很清楚自动修复-Infinity可能改变语句开头statement-leading与链式表达式chained expressions中的解析结果——例如Number.NEGATIVE_INFINITY.toString()若被替换为-Infinity.toString()语义就完全变了见下一个用例。因此规则作者刻意只为NEGATIVE_INFINITY关闭了 autofix。6. 链式调用Number.NEGATIVE_INFINITY.toString()invalid 6const foo Number.NEGATIVE_INFINITY.toString(); // → Prefer -Infinity over Number.NEGATIVE_INFINITY. // 无修复输出此用例正是上一点的最佳佐证-Infinity.toString()会被解析为对Infinity取负再访问toString与原意相悖所以这里只报告、不自动修复需要开发者手动改写为(-Infinity).toString()之类的形式。7. 对象属性值{value: Number.NaN}invalid 7const foo {value: Number.NaN}; // → Prefer NaN over Number.NaN. // 修复输出const foo {value: NaN};错误高亮精确落在属性值Number.NaN上修复也仅替换该值说明修复是节点级的精确文本替换不会影响外层结构。8. 同一表达式内两个错误invalid 8const foo {[Number.POSITIVE_INFINITY]: Number.NEGATIVE_INFINITY};该用例一口气触发两个错误Error 1/2[Number.POSITIVE_INFINITY]→ 替换为[Infinity]输出{ [Infinity]: Number.NEGATIVE_INFINITY }Error 2/2Number.NEGATIVE_INFINITY→ 报告Prefer-InfinityoverNumber.NEGATIVE_INFINITY.无修复输出。快照的Error 1/2、Error 2/2编号表明该行存在多条报告并且每条报告独立决定是否附带修复——修复行为是逐个问题节点计算的互不干扰。9. 带注释的Number /* comment */ .NaNinvalid 9const foo Number /* comment */ .NaN; // → Prefer NaN over Number.NaN. // 无修复输出输入中Number与.NaN之间夹了一个块注释。规则仍能正确识别并报告但放弃了自动修复。这是因为修复采用fixer.replaceText(node, replacement)直接整体替换节点文本一旦节点内部含有注释直接替换会吞掉注释造成信息丢失因此源码里对此做了显式保护详见下文修复条件的三个门槛。源码级原理规则是如何做到上述行为的快照呈现的是结果而 rules/prefer-global-number-constants.js 展示了实现这些行为的全部逻辑核心只有几十行。三条替换映射与统一消息模板const MESSAGE_ID prefer-global-number-constants; const messages { [MESSAGE_ID]: Prefer {{replacement}} over Number.{{property}}., }; const replacements { NaN: NaN, POSITIVE_INFINITY: Infinity, NEGATIVE_INFINITY: -Infinity, };见 rules/prefer-global-number-constants.js所有报错共用同一条消息模板通过data中的property与replacement注入具体名称。replacements对象同时充当白名单只有这三个属性会被报告其他如Number.MAX_SAFE_INTEGER、Number.EPSILON等一概不受影响——这一点与测试文件中的合法用例const foo Number.MAX_SAFE_INTEGER;完全对应。全局引用追踪如何命中Number.NaN规则创建了三个独立的GlobalReferenceTracker分别追踪Number.NaN、Number.POSITIVE_INFINITY、Number.NEGATIVE_INFINITY见 rules/prefer-global-number-constants.js。追踪器的实现位于 rules/utils/global-reference-tracker.js其核心机制把Number.NaN这类点分路径按.拆分成嵌套的 trace mapcreateTraceMap见 global-reference-tracker.js形成{Number: {NaN: true}}的结构借助eslint-community/eslint-utils的ReferenceTracker在全局作用域上迭代所有引用iterateGlobalReferences默认类型为READ读引用通过context.onExit(Program, ...)在程序退出时统一执行追踪listen方法见 global-reference-tracker.js。正因为追踪器把Number.NaN当作全局对象路径来匹配window.Number.NaN、Number[NaN]这些变体写法才能被统一命中这也解释了快照中 invalid 2、invalid 3 的行为。过滤左手边避免误报赋值目标每条引用在被处理前还要过一道filterconst tracker new GlobalReferenceTracker({ object, context, handle: getPropertyProblem, filter: ({node}) !isLeftHandSide(node), });见 rules/prefer-global-number-constants.jsisLeftHandSiderules/utils/is-left-hand-side.js专门检测节点是否处于左手边位置包括赋值表达式与赋值模式的左侧AssignmentExpression/AssignmentPattern自增自减的运算对象UpdateExpression数组解构模式ArrayPattern剩余参数RestElement对象解构的属性值ObjectPattern中的Propertydelete操作的目标。由此可以解释测试文件中的合法用例Number.NaN 1;、Number.POSITIVE_INFINITY || 1;、[Number.NEGATIVE_INFINITY] [];都不会被报告——因为它们是写入而不是读取。而对Number.NaN这类全局对象的属性赋值本来也毫无意义规则选择直接放过而非纠正。修复条件的三个门槛getPropertyProblem见 rules/prefer-global-number-constants.js在决定是否附带修复时依次检查三个条件替换名未被遮蔽通过isReplacementShadowedrules/prefer-global-number-constants.js在当前作用域查找名为NaN或Infinity的变量若存在自定义定义variable.defs.length 0则不修复——否则会把Number.NaN修成用户自己定义的局部NaN造成语义破坏被替换属性不是NEGATIVE_INFINITY如前所述-Infinity的自动修复会改变语句开头与链式表达式的解析语义故一律不修复节点内无注释context.sourceCode.getCommentsInside(node).length 0一旦节点内夹有注释如Number /* comment */ .NaN放弃修复以免注释被替换吞掉。注意第 1 点与快照中有Output的用例invalid 1/2/3/4/7/8 的 Error 1一一对应而第 2、3 点对应的用例invalid 5/6/8 的 Error 2/9在快照中均没有Output字段——快照与源码互相印证行为完全自洽。合法用例理解规则的不干预边界测试文件 test/prefer-global-number-constants.js 中的valid数组从反面划定了规则边界除了上面已分析的左手边与遮蔽场景外还包括已使用全局写法的代码const foo NaN;、const foo Infinity;、const foo -Infinity;非目标静态属性const foo Number.MAX_SAFE_INTEGER;被局部对象遮蔽的Numberconst Number {NaN: 1, ...}后使用Number.NaN——追踪器匹配的是全局Number引用局部遮蔽后自然不命中局部遮蔽的NaN与Infinity含script与module两种sourceTypeconst NaN 1; const value Number.NaN;时即便代码可被报告也会因遮蔽而跳过同时也与不修复逻辑一致避免给出错误的修复建议非全局路径object.Number.NaN成员访问链起点是局部对象object不构成全局引用路径。这些用例共同说明规则的匹配是基于全局引用的精确追踪而不是简单的字符串替换遮蔽、局部变量、写入位置都会被正确识别。与 prefer-number-properties 的冲突关系重要配置提示规则文档末尾有一条醒目的 [!CAUTION]警告并在 prefer-number-properties 的文档 中再次强调本规则强制的是prefer-number-properties规则中checkNaN与checkInfinity选项的相反方向。如果你更偏好Number.NaN、Number.POSITIVE_INFINITY、Number.NEGATIVE_INFINITY写法请禁用本规则并开启那两个选项。也就是说两条规则之间存在直接对立prefer-global-number-constants推荐用全局NaN/Infinity/-Infinityprefer-number-properties的checkNaN: true/checkInfinity: true推荐用静态属性Number.NaN/Number.POSITIVE_INFINITY/Number.NEGATIVE_INFINITY。由于prefer-global-number-constants已包含在recommended与unopinionated预设中如果你在配置中同时开启prefer-number-properties的这两个选项就会在同一份代码上收到互相矛盾的报告。实践中的建议是明确自己的风格取向二选一若坚持Number.*静态属性风格需手动关闭prefer-global-number-constants。如何在本地运行验证想亲自复现快照中的行为可在仓库根目录执行npm test -- --match*prefer-global-number-constants*或直接运行对应测试文件项目使用 AVA 作为测试框架npx ava test/prefer-global-number-constants.js对真实项目应用该规则以 flat config 为例export default [ { plugins: {unicorn: unicornPlugin}, rules: { unicorn/prefer-global-number-constants: error, }, }, ];之后运行npx eslint --fix即可看到快照中展示的自动修复效果。若想调整风格方向可对照 prefer-number-properties 规则文档 配置checkNaN/checkInfinity并记得关闭本规则以避免冲突。小结prefer-global-number-constants是一条例行简单、但边界处理极其考究的规则。通过快照报告 test/snapshots/prefer-global-number-constants.js.md 中 9 个非法用例可以看到Number.NaN与Number.POSITIVE_INFINITY在未被遮蔽、无注释时可安全自动修复Number.NEGATIVE_INFINITY出于解析语义考虑只报错不修复window.Number.NaN、Number[NaN]等变体写法均能被全局引用追踪器识别而赋值、解构等左手边位置则被显式排除。这些行为在 规则源码、全局引用追踪器 与 测试用例 中环环相扣、互相印证快照正是这条完整证据链上最终运行结果的一环。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →