
eslint-plugin-unicorn 规则详解no-impossible-length-comparison 拦截不可能的 .length / .size 比较【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn本篇技术指南围绕 eslint-plugin-unicorn 的no-impossible-length-comparison规则展开讲解它如何识别并报告对数组、字符串.length以及内建集合.size的“必然恒真 / 恒假”比较并通过仓库源码与测试用例剖析其判定逻辑、静态求值能力与边界豁免策略。读完本文你将掌握该规则的全部触发场景、修复方式、豁免机制以及它与其他 unicorn 规则的协同使用姿势。规则概述为什么.length 0一定是 bug在 JavaScript 中数组与字符串的length属性、Set / Map 等内建集合的size属性其语义是“元素个数”因此永远是非负整数。基于这一事实任何与负数边界或“至少为零”的比较结果都是恒定的——要么恒为false要么恒为true。这样的代码通常意味着开发者写错了值或比较运算符。该规则正是为此而生官方描述为Disallow impossible comparisons against.lengthor.size.规则 IDno-impossible-length-comparison问题类型meta.typeproblem适用语言js/js见 规则入口默认配置同时启用 ✅recommended与 ☑️unopinionated两套预设见 readme 规则清单规则的报告消息为This comparison is always {{result}} because .{{property}} is always a non-negative integer.其中{{property}}是length或size{{result}}是恒定的结果true或false消息模板定义在 no-impossible-length-comparison.js。触发场景哪些比较必然恒真 / 恒假规则监听BinaryExpression只要比较的一方是.length/.size成员表达式另一方是可在编译期静态求值的数字就会按“长度非负整数”这一不变量推导结果见 create 入口。以下写法全部会被标记// ❌ 恒为 false长度不可能小于 0 if (array.length 0) {} // ❌ 恒为 false长度不可能等于 -1通常是 indexOf 返回值判断写串了 if (string.length -1) {} // ❌ 恒为 false if (set.size -1) {} // ❌ 恒为 true长度永远 0此判断没有任何意义 if (array.length 0) {}反之以下写法是合理且不会被报告的// ✅ 判断空集合 if (array.length 0) {} // ✅ 判断非空 if (array.length 0) {} // ✅ 判断至少含一个元素 if (set.size 1) {}完整的恒真 / 恒假判定表结合 getConstantResult 的实现当比较对象subject为.length/.size、常量为有限数字value时判定逻辑可归纳为比较运算符触发条件恒定结果value 0falsevalue 0falsevalue 0truevalue 0true/value 0false!/!value 0true注意两个细节常量必须是有限数字typeof value ! number或!Number.isFinite(value)时直接放弃报告NaN、Infinity等不会触发。与的临界点是0length 0恒假、length 0恒真因为 0 本身就是合法的非负整数而、、相等/不等系列以-1为界。操作数位置无关自动翻转运算符.length/.size并不要求一定写在比较符左侧。当它出现在右侧时规则会通过flipOperator映射翻转运算符后再判定见 getComparisonSubject 与 flipOperator 表// ❌ 等价于 array.length -1恒为 false if (-1 array.length) {} // ❌ 等价于 array.length 0恒为 true if (0 array.length) {} // ❌ 等价于 array.length ! -1恒为 true if (-1 ! array.length) {}这些反向写法同样会被识别对应测试见 test/no-impossible-length-comparison.js。静态求值不局限于字面量常量规则并不只认字面量它借助工具函数getStaticValueForControlFlow实现在 rules/utils/get-static-value.js做控制流敏感的静态求值同时识别两类额外来源见 getStaticComparisonValueconst变量的静态值定义后未再被改写的const其初始值可被追踪。已知的全局静态属性通过isKnownStaticProperty白名单识别Math.*与Number.*中的常量成员如Number.MIN_SAFE_INTEGER、Math.PI、Number.EPSILON。由此以下代码全部可被正确识别// ❌ 恒为 falseconst 初始值为 -1 const negativeOne -1; if (array.length negativeOne) {} // ❌ 恒为 false-Number.EPSILON 0 if (array.length -Number.EPSILON) {} // ❌ 恒为 false-Math.PI 0 if (array.length -Math[PI]) {} // ❌ 恒为 true-Number.EPSILON 0 if (array.length -Number.EPSILON) {}对应的测试用例见 test/no-impossible-length-comparison.js。需要说明的是静态求值非常谨慎getStaticValueForControlFlow会拒绝可能产生副作用、包含可变成员访问、或在可求值路径上存在未完成声明TDZ的表达式见 get-static-value.js。因此下列“看似常量”的写法不会误报// 非 const 或条件分支无法静态确定 → 不报告 const alias condition; var condition true; if (array.length (alias ? -1 : lowerBound)) {} // 对象属性被 getter 改写 → 不报告 const object {length: 1}; Object.defineProperty(object, length, {get() { return -1; }}); if (object.length 0) {}上述两条分别对应 测试用例。TypeScript 场景规则内部对所有关键节点都先经过unwrapTypeScriptExpression解包如as断言、satisfies、非空断言!等因此 TS 语法不会干扰识别// ❌ 恒为 falseTS 断言不影响判定 if ((array.length as number) 0) {} // ❌ 恒为 false if (numberarray.length 0) {} // ❌ 恒为 false if ((array.length satisfies number) -1) {} // ❌ 恒为 false非空断言 if (array.length! 0) {}对应测试见 test/no-impossible-length-comparison.js。豁免机制什么时候故意不报告该规则设计上刻意避免对“自定义对象的业务属性”误报共有四道豁免关卡见 create 中的判断。1. 同类对象形状检查自定义对象属性对于dimensions.width dimensions.length 0这类代码length很可能是“物体尺寸”这一领域概念而非集合基数直接报告会误伤。工具函数hasSameObjectShapePropertyCheck实现在 rules/utils/length-or-size.js专门识别这种模式当length/size比较处于一个逻辑表达式中且同一接收者对象还同时读取了depth、height、width这三个“形状属性”之一时规则选择跳过。// ✅ 不报告length 是物体的形状属性 if (dimensions.width dimensions.length 0) {} if (dimensions.height dimensions.size -1) {}但如果是组合进更深层条件、与其他分支混杂则仍会报告// ❌ 报告外层 || fallback 使“形状检查”语义不再成立 if (dimensions.width (dimensions.length 0 || fallback)) {}对应测试见 test/no-impossible-length-comparison.js 与 L214-L217。2. 已知的非集合 length / size数据流分析工具函数isKnownNonCollectionLengthOrSize实现在 rules/utils/length-or-size.js会做轻量数据流分析若某对象的length/size被静态证明不是安全非负整数如初始化为-1、NaN、Infinity或字符串则不报告。// ✅ 不报告length 是业务字段值为 -1 const value {length: -1}; if (value.length 0) {} // ✅ 不报告size 是字符串 const value {size: small}; if (value.size 0) {} // ✅ 不报告length 被改写为 -1且改写发生在读取之前 const object {length: 1}; Object.defineProperty(object, length, {get() { return -1; }}); if (object.length 0) {}该函数还考虑了Object.defineProperty/Object.defineProperties/Object.assign的属性描述符、for...of/for...in循环、条件执行路径isConditionallyExecuted以及 accessor 定义等因素——一旦存在无法静态确定的改写hasUnknownEffect同样豁免见 length-or-size.js。3. 可选链Optional Chaining接收者如果.length/.size通过?.访问其取值可能为undefined不再满足“非负整数”不变量因此一律不报告见 isOptionalChainReceiver// ✅ 不报告array 可能为 undefined if (array?.length 0) {} if ((array?.items).length 0) {} if ((array?.items).metadata.length 0) {}4. 自定义类中的 this / super当接收者是this或super时length/size大概率是自定义类属性而非集合基数同样豁免见 isCustomClassReceiver// ✅ 不报告 if (this.length 0) {} if (this.size 0) {} class Foo extends Bar { method() { if (super.size 0) {} } }其他不会触发的情况除了上述四类豁免还有几种写法天然不满足触发条件也不会被报告// ✅ 与未知变量比较无法静态求值 if (array.length minimumLength) {} // ✅ 与 Infinity 比较非有限数字 if (array.length Number.POSITIVE_INFINITY) {} // ✅ 计算属性名不属于 .length / .size 成员表达式isLengthOrSizeMemberExpression 要求非 computed if (array[length] 0) {} // ✅ 与另一个集合的 size 比较右侧非常量 const modes new Set(); if (array.length modes.size) {} // ✅ 不等判断 0 是合法值 if (array.length ! 0) {}其中“必须是非常量静态值”这一约束意味着array.length modes.size这类集合间比较完全不受影响规则只抓“与数字常量比较”的最典型场景。相关测试见 test/no-impossible-length-comparison.js。使用与配置方式启用规则由于该规则已包含在recommended与unopinionated预设中见 readme 规则清单使用 flat config 时只需引入对应预设即可默认生效// eslint.config.js import unicorn from eslint-plugin-unicorn; export default [ unicorn.configs[recommended], // 或 unicorn.configs[unopinionated] ];若希望单独启用或微调export default [ { plugins: {unicorn}, rules: { unicorn/no-impossible-length-comparison: error, // off 可关闭规则当前无任何可配置选项 }, }, ];无法避免时的处理规则文档明确说明对于确有需要的自定义负值length/size属性不属于上述豁免范畴请使用 ESLint disable 注释// eslint-disable-next-line unicorn/no-impossible-length-comparison if (customObject.length -1) {}同类规则的协同该规则只做“恒真 / 恒假”比较的静态识别不做自动修复无fixable标记。它常与其他 unicorn 规则配合使用例如explicit-length-check强制统一长度判断写法、prefer-set-has、prefer-array-index-of等可共同帮助开发者把“长度/数量判断”写成更明确、无冗余的形式。建议在代码评审中遇到 0、 0、 -1等长度判断时优先思考是否意图用 0/ 0/ 1或改用Array.prototype.includes、Set.prototype.has等更贴合语义的 API。源码结构速览若想深入阅读该规则的完整实现链路可按以下路径追溯规则主体rules/no-impossible-length-comparison.js —— 监听BinaryExpression完成操作数识别、运算符翻转、豁免判断与报告核心工具rules/utils/length-or-size.js ——isLengthOrSizeMemberExpression、isKnownNonCollectionLengthOrSize、hasSameObjectShapePropertyCheck静态求值工具rules/utils/get-static-value.js ——getStaticValueForControlFlow、getStaticValueIfNoSideEffects测试用例test/no-impossible-length-comparison.js —— 覆盖上述全部正例、反例与 TS 场景规则导出rules/index.js ——export {default as no-impossible-length-comparison} from ./no-impossible-length-comparison.js规则文档docs/rules/no-impossible-length-comparison.md。小结no-impossible-length-comparison是一个“小而精”的问题检测规则它基于“.length/.size恒为非负整数”这一语言不变量识别出必然恒真或恒假的比较帮助开发者尽早发现写错的边界值或运算符。其实现亮点在于静态求值与误报规避的平衡——既能识别const、Math.*、Number.*、TS 断言等场景下的常量又能通过对象形状检查、数据流分析、可选链与this/super豁免避免对自定义业务属性误报。作为recommended预设的默认成员它无需任何配置即可为项目提供这一层防御是值得默认开启的低成本规则之一。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考