eslint-plugin-unicorn 规则实战:prefer-number-properties 统一 Number 静态方法与属性
2026/9/18 9:31:52 网站建设 项目流程

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规则展开,讲解它如何将全局数字函数(parseIntparseFloatisNaNisFinite)与可选开启的全局常量(NaNInfinity-Infinity)统一收敛到Number构造函数的静态成员上。读完本文,你将掌握该规则的全部检查维度、checkNaNcheckInfinity两个选项的配置方法、自动修复与手动建议(suggestion)的边界条件,以及它与prefer-global-number-constantsprefer-number-coercion等相邻规则之间的协作与冲突关系。

规则背景:为什么用Number静态成员替代全局数字函数

ECMAScript 2015(ES6)出于一致性的考虑,将原本散落在全局作用域的数字处理函数迁移到了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, };
  • parseIntparseFloatNaNInfinity:标记为 "safe"(安全替换),因为Number.parseIntNumber.parseFloatNumber.NaNNumber.POSITIVE_INFINITY/Number.NEGATIVE_INFINITY与对应全局形式在语义上完全一致,可以放心自动修复。
  • isNaNisFinite:标记为 "unsafe"(不安全替换),因为全局isNaN/isFiniteNumber.isNaN/Number.isFinite存在微妙的语义差异(见下文),不能无条件自动改写。

该规则被收录于recommendedunopinionated两个配置中,注册位置见 rules/index.js。

检查清单:五个被约束的全局成员

规则共约束 5 个全局成员的用法:

全局形式推荐写法可自动修复?
parseInt(带非十进制 radix)Number.parseInt()✅ 是(fixable)
parseFloatNumber.parseFloat()✅ 是(fixable)
isNaNNumber.isNaN()⚠️ 仅当参数可确定为 number 时自动修复,否则为建议
isFiniteNumber.isFinite()⚠️ 仅当参数可确定为 number 时自动修复,否则为建议
NaN(开启checkNaN后)Number.NaN✅ 是(fixable)
Infinity(开启checkInfinity后)Number.POSITIVE_INFINITY✅ 是(fixable)
-Infinity(开启checkInfinity后)Number.NEGATIVE_INFINITY✅ 是(fixable)

isNaN/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.js):isNaN(10)isNaN(foo - 1)isNaN(foo.length)可自动修复,而isNaN(foo)isNaN(foo + bar)isNaN(...foo)仅给建议。

示例:基本用法与错误修正

parseIntNumber.parseInt

// ❌ const foo = parseInt('10', 2); // ✅ const foo = Number.parseInt('10', 2);

parseFloatNumber.parseFloat

// ❌ const foo = parseFloat('10.5'); // ✅ const foo = Number.parseFloat('10.5');

isNaNNumber.isNaN

// ❌ const foo = isNaN(10); // ✅ const foo = Number.isNaN(10);

isFiniteNumber.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-Infinity
checkNaNbooleanfalse是否检查全局NaN

checkInfinity

开启后,规则会约束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]checkNaNcheckInfinity强制的是与 prefer-global-number-constants相反的方向,而后者默认已在recommendedunopinionated配置中开启。如果你更偏好Number.NaNNumber.POSITIVE_INFINITYNumber.NEGATIVE_INFINITY这种常量形式,请禁用prefer-global-number-constants,避免两条规则对同一处代码发出冲突报告。

相邻规则协作:prefer-global-number-constants 与 prefer-number-coercion

这三个规则共同构成了数字 API 风格管理的完整矩阵,建议一起理解:

  • prefer-global-number-constants:与prefer-number-propertiescheckNaN/checkInfinity选项方向相反,偏好更短、更易读的全局形式NaNInfinity-Infinity,而不是Number.NaNNumber.POSITIVE_INFINITYNumber.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-utilsReferenceTracker构建,在Program节点退出时(context.onExit)扫描全局作用域中的所有引用,自动完成:

  1. 构建追踪映射:把待检查的全局对象名(parseIntparseFloatNaNInfinity以及条件启用的isNaN/isFinite)转换成 trace map;
  2. 过滤:通过filter回调跳过左侧写入(isLeftHandSide)、delete -Infinity、无 radix/base-10 的parseInt等场景;
  3. 处理:调用getPropertyProblem生成诊断信息——安全场景直接挂fix(可--fix自动修复),不安全场景挂suggest(editor suggestion 手动应用)。

两条消息模板定义在 prefer-number-properties.js:

  • errorPrefer \Number.{{property}}` over `{{description}}`.`
  • suggestionReplace \{{description}}` with `Number.{{property}}`.`

正因为使用了全局引用追踪而非简单的标识符匹配,规则天然具备遮蔽(shadowing)感知能力:只要某个局部作用域声明了自己的parseIntNaN等同名标识符,规则就不会误伤该局部用法。

实测验证:如何在自己的项目里启用并观察效果

  1. 安装:确保项目已安装eslint-plugin-unicorn并在 ESLint 配置中启用该插件。
  2. 使用推荐配置recommended/unopinionated配置已默认开启本规则(此时仅约束四个函数形式,NaNInfinity不检查)。
  3. 按需开启常量检查:在规则配置中传入{"checkInfinity": true}和/或{"checkNaN": true};若同时使用recommended配置,需同步禁用 prefer-global-number-constants 以免冲突。
  4. 执行修复:运行npx eslint --fix应用自动修复;无法自动修复的场景(如参数类型不确定的isNaN/isFinite)会在编辑器中以 suggestion 形式出现,手动应用即可。
  5. 回归验证:项目的测试套件在 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),仅供参考

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

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

立即咨询