十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

eslint-plugin-unicorn 规则深度解析:用 prefer-number-is-safe-integer 消除不安全的整数判断

eslint-plugin-unicorn 规则深度解析:用 prefer-number-is-safe-integer 消除不安全的整数判断 eslint-plugin-unicorn 规则深度解析用 prefer-number-is-safe-integer 消除不安全的整数判断【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn本文以 eslint-plugin-unicorn 项目中的prefer-number-is-safe-integer规则文档docs/rules/prefer-number-is-safe-integer.md为核心结合规则源码rules/prefer-number-is-safe-integer.js与测试用例test/prefer-number-is-safe-integer.js系统讲解 JavaScript 中整数判断的精度陷阱以及该规则如何在Number.isInteger()、value % 1 0、Math.trunc()/Math.floor()比较、Lodash/UnderscoreisInteger()等常见写法中识别隐患并提示改为Number.isSafeInteger()。读完本文你将理解安全整数范围的含义、规则覆盖与刻意忽略的模式边界、为什么它只提供建议而非自动修复以及如何结合源码判断哪些写法会被报告、哪些不会。规则速览它到底检查什么prefer-number-is-safe-integer是一条suggestion建议类型的规则核心目的是推广使用Number.isSafeInteger()替代各种只判断整数性、不判断可精确表示性的写法。从规则元数据rules/prefer-number-is-safe-integer.js可以看到type: suggestion属于建议类规则不直接改变运行时行为recommended: true该规则在recommended配置中默认开启readme.md 的规则总表中用 ✅ 标注而在unopinionated配置中保持关闭hasSuggestions: true通过editor suggestions编辑器建议提供手动可用的修复而非自动修复languages: [js/js]仅针对 JavaScriptTypeScript 需要借助解析器支持测试中可见 TS 断言场景。从源码可见规则实际定义了四条消息 ID覆盖两类问题rules/prefer-number-is-safe-integer.jsprefer-number-is-safe-integer/error针对Number.isInteger()调用的主报告prefer-number-is-safe-integer/suggestion对应的替换建议prefer-number-is-safe-integer/integer-check-error针对% 1 0、Math.trunc()/Math.floor()比较、Lodash/Underscore 调用等通用整数检查的报告prefer-number-is-safe-integer/integer-check-suggestion对应的替换建议。为什么要用 Number.isSafeInteger()精度问题的本质Number.isSafeInteger()检查一个值既是整数又能被精确表示即落在安全整数范围[-(2 ** 53 - 1), 2 ** 53 - 1]内。而Number.isInteger()只判断是否为整数对超出该范围、已无法精确保存的大整数也会返回true——这几乎从来不是开发者想要的语义。原文档给出的示例很直观地说明了差异// ❌ // This is problematic because Numbers larger than 2^53 - 1 lose precision const largeNumber 9007199254740992; // 2^53 Number.isInteger(largeNumber); // true (misleading!) largeNumber 9007199254740993; // true (precision lost!) // ✅ Number.isSafeInteger(largeNumber); // false (correctly identifies the issue)当数值超过2^53 - 1时IEEE 754 双精度浮点无法区分相邻整数9007199254740992与9007199254740993在内存中相同。此时Number.isInteger()给出的true具有误导性——它只证明类型上是整数无法证明数值本身可靠。典型的业务场景是 ID、时间戳、序列号等数据// ❌ function processId(id) { if (!Number.isInteger(id)) { throw new Error(Invalid ID); } // id could still be too large to represent exactly } // ✅ function processId(id) { if (!Number.isSafeInteger(id)) { throw new Error(Invalid ID); } // id is guaranteed to be safely representable }规则覆盖的四类写法原文档明确指出该规则不仅报告Number.isInteger()还报告常见的整数检查写法value % 1 0及对称形式0 value % 1Math.trunc(value) value与Math.floor(value) value含对称形式value Math.trunc(value)Lodash / Underscore 的isInteger()/isSafeInteger()调用1. Number.isInteger() 调用这是最直接的命中场景。源码在CallExpression监听器里通过isMethodCall精确匹配rules/prefer-number-is-safe-integer.js要求调用对象是裸的全局NumbersourceCode.isGlobalReference保证不是window.Number、globalThis.Number或用户自定义的Number变量、方法名为isInteger、非可选调用、非可选成员、非计算属性。命中后仅将isInteger标识符替换为isSafeInteger其余参数原样保留。// ❌ if (!Number.isInteger(index)) { throw new Error(Expected an integer.); } // ✅ if (!Number.isSafeInteger(index)) { throw new Error(Expected a safe integer.); }快照测试test/snapshots/prefer-number-is-safe-integer.js.md展示了真实输出错误定位在isInteger标识符上提示语为 PreferNumber.isSafeInteger()overNumber.isInteger().并附建议 ReplaceNumber.isInteger()withNumber.isSafeInteger().替换结果如!Number.isInteger(x)→!Number.isSafeInteger(x)。2. 取模检查value % 1 0value % 1 0是流传已久的手写整数检查源码通过getModuloCheckArgument与getModuloIntegerCheckArgument识别rules/prefer-number-is-safe-integer.js要求是%二元表达式、右侧为字面量1、并与字面量0做严格相等比较左右两侧均可。object.value % 1 0、(foo, bar) % 1 0这类成员表达式和序列表达式同样会命中。// ❌ if (value % 1 0) { console.log(Integer); } // ✅ if (Number.isSafeInteger(value)) { console.log(Safe integer); }注意测试用例中明确排除了value % 1 0宽松相等、value % 1 ! 0否定比较和(value | 0) value位运算检查这些都不会被报告。3. Math 方法比较Math.trunc() / Math.floor()Math.trunc(value) value与Math.floor(value) value同样会被识别。源码getMathIntegerCheckArgumentrules/prefer-number-is-safe-integer.js限定了严格条件必须是Math.trunc/Math.floor方法调用mathIntegerCheckMethods数组只含这两个参数个数必须恰好为 1Math必须是全局引用不可选调用、不可选成员、不可计算属性。getMathComparisonIntegerCheckArgumentrules/prefer-number-is-safe-integer.js进一步要求比较两侧通过isSameReference判定为同一引用如Math.trunc(object.value) object.value否则不报告。以下形式均会被报告Math.floor(value) value、value Math.floor(value)、Math.trunc(value) value、value Math.trunc(value)。有意忽略的类似模式包括Math.round(value) value取整方向不同语义不等价以及Number.parseInt(value, 10) value这类字符串解析比较。4. Lodash / Underscore 的 isInteger() / isSafeInteger()规则同样覆盖工具库调用。源码中的lodashObjects常量定义了[_, lodash, underscore]三种对象名rules/prefer-number-is-safe-integer.jsgetLodashIntegerCheckArgumentrules/prefer-number-is-safe-integer.js匹配这些对象上的isInteger与isSafeInteger方法1 个参数、非可选、非计算属性。// ❌ if (_.isInteger(value)) { console.log(Integer); } // ✅ if (Number.isSafeInteger(value)) { console.log(Safe integer); }_.isSafeInteger(value)、lodash.isSafeInteger(value)、underscore.isSafeInteger(value)也会被报告——因为既然已经显式表达了安全整数语义直接使用原生Number.isSafeInteger()更为简洁统一。一个细节是报告 Lodash 写法前源码会调用isGlobalNumberAvailablerules/prefer-number-is-safe-integer.js确认当前作用域内的Number未被局部变量遮蔽确保替换后的代码语义安全。为什么只提供建议而不自动修复原文档强调该规则仅提供建议suggestion不做自动修复automatic fix因为各类检查并不完全等价。两个关键反例Number.isInteger(2 ** 53)返回true而Number.isSafeInteger(2 ** 53)返回false——直接替换会改变程序行为[[1]] % 1 0返回true隐式强制转换后取模而Number.isSafeInteger([[1]])返回false——% 1检查会做类型强制转换替换后行为不同。因此在应用建议前必须逐个审查具体场景。尤其要注意否定检查!Number.isInteger(x)换成!Number.isSafeInteger(x)后非整数的判定集合会扩大更多值被判为不合法例如原本通过!Number.isInteger(2 ** 53)的值将变为不通过这可能让校验逻辑更严格、也可能意外拦截合法数据。源码中的工程细节注释保护与表达保持规则的createIntegerCheckProblem与hasCommentsOutsideNode[rules/prefer-number-is-safe-integer.js](https://link.gitcode.com/i/5f1edce8d963264272b8874e5f397501#L24-L31, L119-L135)处理了一个容易被忽略的边界当被检查的表达式中带有注释时如value /* comment */ % 1 0、_.isInteger(/* comment */ value)、Math.trunc(/* comment */ value) value替换整段表达式会导致注释丢失因此规则仍然报告错误但不提供建议——测试用例中明确标注了 Reported without suggestion to avoid dropping comments。对于Number.isInteger(/* comment */ x)由于替换只作用于isInteger属性名注释安全保留所以照常给出建议。此外getExpressionTextrules/prefer-number-is-safe-integer.js会把序列表达式用括号包裹如(foo, bar) % 1 0→Number.isSafeInteger((foo, bar))保证替换后运算顺序不变。精确识别不会误报的边界情况综合测试文件test/prefer-number-is-safe-integer.js中的 valid 用例可以梳理出规则刻意放过的完整清单非调用形态Number.isInteger仅引用、Number.isInteger.bind(Number)、new Number.isInteger(x)对象被遮蔽或非全局const Number {...}、import Number from number、函数参数function foo(Number)、window.Number.isInteger(x)、globalThis.Number.isInteger(x)、NotNumber.isInteger(x)可选调用 / 可选成员 / 计算属性Number.isInteger?.(x)、Number?.isInteger(x)、NumberisInteger、NumberisInteger大小写不匹配Number.isinteger(x)比较形式不合规value % 1 0宽松、value % 1 ! 0、0 ! value % 1、Math.trunc(value) ! value、Math.floor(value) value非等价比较Math.floor(value) otherValue两侧引用不同刻意忽略的模式(value | 0) value位运算检查、Number.parseInt(value, 10) value、Math.round(value) valueLodash 变体_.isInteger?.(value)、_?.isInteger(value)、_isInteger、_.isInteger(...value)、_.isInteger(value, extra)。在 TypeScript 场景下测试覆盖了Number.isInteger(x as number)、Number.isInteger(x!)非空断言必须在建议中保留、Math.trunc(value as number) value、Math.trunc(value!) value、Math.trunc(value) value as number等断言写法证明建议生成时会原样保留类型断言与运算符。如何在项目中启用与使用该规则已在recommended配置中默认开启见 readme.md 规则总表中prefer-number-is-safe-integer一行的 ✅ 标注使用 ESLint flat config 时直接继承 recommended 配置即可生效无需额外配置import unicorn from eslint-plugin-unicorn; export default [ unicorn.configs.recommended, // ...其他配置 ];若使用旧版 eslintrc 风格则对应plugin:unicorn/recommended扩展。该规则没有任何可配置选项行为完全由源码内置的匹配逻辑决定。启用后当编辑器如 VS Code集成了 ESLint 建议功能时报告的代码行下方会显示建议提示 标记开发者可逐个审阅并手动应用替换这正是该规则设计为 suggestion 而非 fix 的价值所在——让Number.isSafeInteger()的替换决策始终保留在人的判断中。小结prefer-number-is-safe-integer通过识别四类整数检查写法Number.isInteger()、% 1 0、Math.trunc()/Math.floor()引用比较、Lodash/UnderscoreisInteger()/isSafeInteger()引导开发者采用Number.isSafeInteger()这一语义更严谨的 API从而规避2^53 - 1之外的大整数精度陷阱。它刻意只提供建议而非自动修复并精确豁免了位运算、parseInt、Math.round等不强制转换的模式同时在带注释表达式上放弃建议以保证注释不丢失——这些细节都值得在审计自己代码库中的整数校验逻辑时参照完整测试矩阵见 test/prefer-number-is-safe-integer.js 与快照 test/snapshots/prefer-number-is-safe-integer.js.md。对于处理 ID、时间戳、金额等数值型数据的项目这是 recommended 配置中值得优先理解并落地的规则之一。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表