ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

eslint-plugin-unicorn 规则实战:prefer-number-properties 统一 Number 静态方法与属性

eslint-plugin-unicorn 规则实战:prefer-number-properties 统一 Number 静态方法与属性 eslint-plugin-unicorn 规则实战prefer-number-properties 统一 Number 静态方法与属性【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn本篇技术指南围绕 eslint-plugin-unicorn 的unicorn/prefer-number-properties规则展开讲解它如何将全局数字函数parseInt、parseFloat、isNaN、isFinite与可选开启的全局常量NaN、Infinity、-Infinity统一收敛到Number构造函数的静态成员上。读完本文你将掌握该规则的全部检查维度、checkNaN与checkInfinity两个选项的配置方法、自动修复与手动建议suggestion的边界条件以及它与prefer-global-number-constants、prefer-number-coercion等相邻规则之间的协作与冲突关系。规则背景为什么用Number静态成员替代全局数字函数ECMAScript 2015ES6出于一致性的考虑将原本散落在全局作用域的数字处理函数迁移到了Number构造函数上并在此基础上做了小幅改进。prefer-number-properties规则正是为了贯彻这一语言演进方向限制代码中对全局数字 API 的使用引导开发者统一书写为Number.*静态成员形式。从 规则实现 可以看到规则内部把需要追踪的全局对象划分成了两组const globalObjects { // Safe to replace with Number properties parseInt: true, parseFloat: true, NaN: true, Infinity: true, // Unsafe to replace with Number properties isNaN: false, isFinite: false, };parseInt、parseFloat、NaN、Infinity标记为 safe安全替换因为Number.parseInt、Number.parseFloat、Number.NaN、Number.POSITIVE_INFINITY/Number.NEGATIVE_INFINITY与对应全局形式在语义上完全一致可以放心自动修复。isNaN、isFinite标记为 unsafe不安全替换因为全局isNaN/isFinite与Number.isNaN/Number.isFinite存在微妙的语义差异见下文不能无条件自动改写。该规则被收录于recommended与unopinionated两个配置中注册位置见 rules/index.js。检查清单五个被约束的全局成员规则共约束 5 个全局成员的用法全局形式推荐写法可自动修复parseInt带非十进制 radixNumber.parseInt()✅ 是fixableparseFloatNumber.parseFloat()✅ 是fixableisNaNNumber.isNaN()⚠️ 仅当参数可确定为 number 时自动修复否则为建议isFiniteNumber.isFinite()⚠️ 仅当参数可确定为 number 时自动修复否则为建议NaN开启checkNaN后Number.NaN✅ 是fixableInfinity开启checkInfinity后Number.POSITIVE_INFINITY✅ 是fixable-Infinity开启checkInfinity后Number.NEGATIVE_INFINITY✅ 是fixableisNaN/isFinite的语义差异决定了修复方式全局isNaN(value)与isFinite(value)在执行判断前会先把参数强制转换为 number而Number.isNaN(value)与Number.isFinite(value)不做任何类型转换。例如isNaN(foo)返回true字符串被转成NaN而Number.isNaN(foo)返回false字符串不是 number 类型直接判为非 NaN。因此源码在 is-number.js 的辅助下实现了isCallWithNumberArgument判定见 prefer-number-properties.js只有当调用参数被证明一定是 number 时数字字面量、Number()调用、数学运算结果、带number类型注解的 TypeScript 标识符、as number/satisfies number断言等重写才是安全的规则才会给出自动修复否则只提供 editor suggestion 供手动确认。这一点在测试中有非常详细的对照见 test/prefer-number-properties.jsisNaN(10)、isNaN(foo - 1)、isNaN(foo.length)可自动修复而isNaN(foo)、isNaN(foo bar)、isNaN(...foo)仅给建议。示例基本用法与错误修正parseInt→Number.parseInt// ❌ const foo parseInt(10, 2); // ✅ const foo Number.parseInt(10, 2);parseFloat→Number.parseFloat// ❌ const foo parseFloat(10.5); // ✅ const foo Number.parseFloat(10.5);isNaN→Number.isNaN// ❌ const foo isNaN(10); // ✅ const foo Number.isNaN(10);isFinite→Number.isFinite// ❌ const foo isFinite(10); // ✅ const foo Number.isFinite(10);边界情况规则刻意放行的写法规则不是无脑报错以下写法均视为合法1. 无 radix 或 base-10 的parseInt不受此规则约束// ✅ 正确写法 const foo Number.parseInt(10, 2);// ✅ 无 radix / base-10 调用由 prefer-number-coercion 处理 const foo parseInt(10, 10);实现中的isBase10OrNoRadixParseIntCall见 prefer-number-properties.js会跳过parseInt(value)无第二个参数以及 radix 静态求值等于10的调用radix 为0时运行时也按十进制处理但规则仍会报告并建议改写测试见 test/prefer-number-properties.js。原因在于base-10 的parseInt()与parseFloat()推荐使用Number()直接转换这由另一个规则 prefer-number-coercion 负责语义略有不同——parseFloat(50px)提取数字前缀得到50而Number(50px)解析整个字符串得到NaN。如果你希望强制要求显式 radix可搭配 ESLint 内置的radix规则使用。2. 从Number解构出来的标识符不再报告// ✅ const {parseInt} Number; const foo parseInt(10, 2);这里局部变量parseInt已经遮蔽shadow了全局标识符全局引用追踪器不会命中。3. 数值零检测中的Infinity在未开启checkInfinity时不报告// ✅ const isPositiveZero value value 0 1 / value Infinity;// ✅ const isNegativeZero value value 0 1 / value -Infinity;4. 其他放行场景从测试用例test/prefer-number-properties.js可以归纳出规则还会放行标识符被遮蔽局部变量、函数参数、解构const {parseInt} Number、类方法等任何形式的 shadowing纯写入而非读取global.isFinite Number.isFinite;、解构赋值目标等左侧写入位置删除操作delete global.isFinite;TypeScript 枚举成员如export enum NumberSymbol { Decimal, NaN }中的NaN成员以及declare var NaN: number;等声明文件场景见 test/prefer-number-properties.js。配置选项checkInfinity 与 checkNaN选项类型为object均默认关闭默认值定义见 prefer-number-properties.js选项类型默认值作用checkInfinitybooleanfalse是否检查全局Infinity与-InfinitycheckNaNbooleanfalse是否检查全局NaNcheckInfinity开启后规则会约束Infinity与-Infinity的用法/* eslint unicorn/prefer-number-properties: [error, {checkInfinity: true}] */ // ❌ const foo Infinity; // ✅ const foo Number.POSITIVE_INFINITY;// ❌ const foo -Infinity; // ✅ const foo Number.NEGATIVE_INFINITY;实现中通过isNegative判断Infinity是否被一元负号包裹见 prefer-number-properties.js从而决定改写为POSITIVE_INFINITY还是NEGATIVE_INFINITY对-Infinity的修复会整段替换外层一元表达式并调用fixSpaceAroundKeyword处理关键字周围空白见 prefer-number-properties.js例如return-Infinity这类紧凑写法也能被安全修复。同时存在一个边界守卫delete -Infinity即delete -Infinity这种对-Infinity的删除表达式不会被误报isDeletedNegativeInfinity见 prefer-number-properties.js。checkNaN开启后规则会约束NaN的用法/* eslint unicorn/prefer-number-properties: [error, {checkNaN: true}] */ // ❌ const foo NaN; // ✅ const foo Number.NaN;从测试快照test/prefer-number-properties.js可以看到开启后规则覆盖的场景相当全面bar[NaN]、{NaN}、{NaN: NaN}、{foo NaN}默认值、NaN.toString()、class Foo3 {[NaN] 1}等均会被改写为Number.NaN。[!CAUTION]checkNaN与checkInfinity强制的是与 prefer-global-number-constants相反的方向而后者默认已在recommended与unopinionated配置中开启。如果你更偏好Number.NaN、Number.POSITIVE_INFINITY、Number.NEGATIVE_INFINITY这种常量形式请禁用prefer-global-number-constants避免两条规则对同一处代码发出冲突报告。相邻规则协作prefer-global-number-constants 与 prefer-number-coercion这三个规则共同构成了数字 API 风格管理的完整矩阵建议一起理解prefer-global-number-constants与prefer-number-properties的checkNaN/checkInfinity选项方向相反偏好更短、更易读的全局形式NaN、Infinity、-Infinity而不是Number.NaN、Number.POSITIVE_INFINITY、Number.NEGATIVE_INFINITY。当替换后的全局标识符未被遮蔽时前两者可自动修复Number.NEGATIVE_INFINITY因涉及语句开头与链式表达式的解析变化只报告不自动修复。prefer-number-coercion负责parseFloat()与 base-10parseInt()→Number()的转换建议如Math.trunc(Number(value))与prefer-number-properties对非十进制parseInt的处理形成互补两者覆盖的 radix 场景不重叠。实现原理GlobalReferenceTracker 的全局引用追踪该规则没有遍历 AST 手动检查而是复用了项目内的GlobalReferenceTracker工具rules/utils/global-reference-tracker.js。它基于eslint-community/eslint-utils的ReferenceTracker构建在Program节点退出时context.onExit扫描全局作用域中的所有引用自动完成构建追踪映射把待检查的全局对象名parseInt、parseFloat、NaN、Infinity以及条件启用的isNaN/isFinite转换成 trace map过滤通过filter回调跳过左侧写入isLeftHandSide、delete -Infinity、无 radix/base-10 的parseInt等场景处理调用getPropertyProblem生成诊断信息——安全场景直接挂fix可--fix自动修复不安全场景挂suggesteditor suggestion 手动应用。两条消息模板定义在 prefer-number-properties.jserrorPrefer \Number.{{property}} over {{description}}.suggestionReplace \{{description}} with Number.{{property}}.正因为使用了全局引用追踪而非简单的标识符匹配规则天然具备遮蔽shadowing感知能力只要某个局部作用域声明了自己的parseInt、NaN等同名标识符规则就不会误伤该局部用法。实测验证如何在自己的项目里启用并观察效果安装确保项目已安装eslint-plugin-unicorn并在 ESLint 配置中启用该插件。使用推荐配置recommended/unopinionated配置已默认开启本规则此时仅约束四个函数形式NaN与Infinity不检查。按需开启常量检查在规则配置中传入{checkInfinity: true}和/或{checkNaN: true}若同时使用recommended配置需同步禁用 prefer-global-number-constants 以免冲突。执行修复运行npx eslint --fix应用自动修复无法自动修复的场景如参数类型不确定的isNaN/isFinite会在编辑器中以 suggestion 形式出现手动应用即可。回归验证项目的测试套件在 test/prefer-number-properties.js 中对全部修复与建议场景含 TypeScript parser、shadowing、Infinity正负号、边界语法做了快照覆盖可作为行为基准参考。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进