eslint-plugin-unicorn no-uncalled-method 规则详解:从误报边界到 Array/String 方法引用的静态检测

eslint-plugin-unicorn no-uncalled-method 规则详解:从误报边界到 Array/String 方法引用的静态检测 eslint-plugin-unicorn no-uncalled-method 规则详解从误报边界到 Array/String 方法引用的静态检测【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn忘记给方法调用加括号代码不会报错而是把「方法函数本身」赋给了变量——例如const sorted array.sort得到的是一个函数而非排序结果。eslint-plugin-unicorn 的no-uncalled-method规则规则文档、规则实现正是针对这类隐蔽 bug 的静态检测当接收者receiver可以被静态识别为Array或String时对「引用而未调用」的方法成员表达式报告problem级别错误。本文基于规则实现与 46 组快照测试用例完整拆解它的检测范围、误报防护机制与 TypeScript 支持细节。规则定位与配置规则元信息定义在 rules/no-uncalled-method.js 的meta中类型type: problem视为真实 bug而非风格建议所属配置集在recommended配置中默认开启在unopinionated配置中禁用见 规则文档 自动生成的头部说明错误信息Call \{{method}}() instead of referencing the method.其中{{method}}由消息 IDno-uncalled-method 的模板填充L11-L14适用语言languages: [js/js]但借助类型感知测试与 TypeScript 解析器扩展同样覆盖.ts场景。官方文档给出的核心用法对比// ❌ const sorted array.sort;// ✅ const sorted array.sort();// ❌ function normalize(string) { return string.toLowerCase; }// ✅ function normalize(string) { return string.toLowerCase(); }注意规则不提供自动修复报告后需要手动补上()。检测原理以 MemberExpression 为切入点的两层判断create函数只注册了一个 AST 监听器L259-L286context.on(MemberExpression, node { if (shouldSkip(node, context)) { return; } const method getPropertyName(node, sourceCode.getScope(node)); if (!method) { return; } if (!shouldReport({ receiver: node.object, method, context })) { return; } return { node: node.property, messageId: MESSAGE_ID, data: {method} }; });处理逻辑分为两层shouldSkipL236-L242过滤掉「引用方法本身」的合法场景这是控制误报的第一道闸门shouldReportL244-L254确认「方法名属于已知Array/String方法集」且「接收者确实是数组/字符串」两道条件同时满足才报告。命中时报错节点锚定在node.property方法名本身所以快照中高亮位置只覆盖方法名例如 1 | const method [1, 2, 3].sort | ^^^^ Call sort() instead of referencing the method.方法白名单覆盖了迭代器辅助方法检测范围由两个Set决定而非「任何方法」arrayMethodsL16-L55at、concat、copyWithin、entries、every、fill、filter、find、findIndex、findLast、findLastIndex、flat、flatMap、forEach、includes、indexOf、join、keys、lastIndexOf、map、pop、push、reduce、reduceRight、reverse、shift、slice、some、sort、splice、toLocaleString、toReversed、toSorted、toSpliced、toString、unshift、values、with——注意其中包含了toReversed/toSorted/toSpliced/with等迭代器提案的不可变方法。stringMethodsL57-L107从anchor、big、blink等历史遗留方法到isWellFormed、toWellFormed等较新方法共 50 个。接收者识别语法、类型标注、命名约定与 const 别名链shouldReport之所以只对「已知接收者」报告是因为接收者判定L171-L175综合了多种静态信息来源const isArrayReceiver (node, context) isNamedOrConstAlias(node, array, context) || isArray(node, context); const isStringReceiver (node, context) isNamedOrConstAlias(node, string, context) || isString(node, context);isArray/isString工具来自 rules/utils/index.js 导出的is-array.js、is-string.js从源码结构看负责识别字面量[]、[1,2,3]、、模板字符串与Array.from(...)、Array.of(...)等静态可判定为数组/字符串的表达式命名约定isNamedOrConstAlias(node, array, ...)识别命名为array或string的变量L136-L169const别名链isNamedOrConstAlias会沿着「单一定义的const变量」递归回溯初始值visitedVariables防止环路例如const alias array; const method alias.shift依然被检出快照用例 invalid(10)而let/var别名因可被重新赋值而不追踪——测试中let alias array; const method alias.reverse属于合法不报test/no-uncalled-method.js#L57-L58TypeScript 类型标注string[]、Arraystring、readonly string[]、ReadonlyArraystring、string类型的变量引用方法均被检出快照 invalid(38)~invalid(44)而显式标注为{reverse: Function}的对象类型则不报test/no-uncalled-method.js#L68-L69string[]上引用split、string上引用reverse这类「方法与类型不匹配」的引用也不报——说明判定是「方法集 × 接收者类型」的交叉验证。规则文档docs/rules/no-uncalled-method.md#L12-L14对此的表述与实现一致This rule checks knownArrayandStringmethods when the receiver can be identified from syntax, type annotations, parser type information, conventional variable names likearrayandstring, orconstaliases of those. Unknown receivers are ignored to avoid false positives.误报防护shouldSkip 的六类豁免场景shouldSkipL236-L242依次检查以下情形任一命中即跳过1. 它本来就是被调用的calleeisCalleeL190-L200判断节点是否是CallExpression/NewExpression的callee并通过getOutermostExpression先穿透透明包装见下节因此array.sort.call(array)、array?.reverse()均合法。2. 作为函数引用传给 call/apply/bindisCallableReferenceL202-L215识别array.reverse.call/.apply/.bind且最终被调用callableReferenceMethods为apply、bind、callL109-L113。文档中的 ✅ 示例array.sort.call(array);即源于此。但注意边界const method array.reverse.call取.call本身而非调用它仍会报reverse()快照 invalid(28)。3. Reflect.apply 的第一个参数isReflectApplyArgumentL217-L225豁免Reflect.apply(fn, array.reverse, [])这种「以函数引用形式传参」的场景测试 valid 组 L27-L30。但快照 invalid(26)/(27) 展示了另一个边界当方法引用出现在第 2/3 个参数位Reflect.apply(fn, array.reverse, [])时仍会报告——因为该位置按Reflect.apply语义本应是 this 对象/参数数组方法引用大概率是写错了位置。4. typeof 判断isTypeofArgumentL227-L234豁免typeof array.reverse function这类鸭子类型检查文档 ✅ 示例typeof string.toLowerCase function;即对应此场景。5. 左值位置借助 rules/utils/is-left-hand-side.jsarray.reverse fn、array.reverse、delete array.reverse、数组/对象解构模式等「把成员表达式当属性来读写」的场景全部豁免测试 valid 组 L31-L34。6. 原型方法引用isPrototypeMethodL177-L188豁免Array.prototype.reverse、String.prototype.toLowerCase含Array[prototype].reverse计算属性写法因为从原型上取方法引用是常见且有意义的操作测试 valid 组 L64-L67。透明包装的穿透可选链、括号与 TS 类型包装getOutermostExpressionL125-L134配合isTransparentExpressionWrapperL119-L123向上穿透ChainExpression、ParenthesizedExpression、TSInstantiationExpression以及 TS 表达式包装TSAsExpression、TSSatisfiesExpression、TSNonNullExpression、TSTypeAssertion定义于 rules/utils/unwrap-typescript-expression.js。这带来一组对称的行为const method array?.reverse会报告可选链只是包装引用本身没变快照 invalid(12)(array.reverse as () void)()不报穿透包装后它是被调用的 calleetest/no-uncalled-method.js#L72-L76但(array.reverse as unknown)、array.mapstring、(string satisfies unknown).trim这类「包装后仍只是引用」的表达式会报告高亮范围只落在方法名上快照 invalid(30)~invalid(34)。完整快照行为速览快照报告数据文件为test/snapshots/no-uncalled-method.js.snap由 AVA 生成收录了 test/no-uncalled-method.js 中全部 46 个 invalid 用例覆盖的接收者形态包括接收者形态快照用例说明数组字面量invalid(1)-(2)[].reverse、[1,2,3].sort语法直接判定Array.from/Array.ofinvalid(3)-(4)静态可推断为数组命名变量array/stringinvalid(5)-(7)、(11)、(15)-(25)命名约定 常规变量const别名含array!非空断言别名invalid(8)-(10)、(17)-(18)、(37)别名链回溯条件判断位置invalid(13)if (array.pop) {}条件中的方法引用计算属性invalid(14)array[reverse]getPropertyName解析字符串键模板字符串invalid(16)text.trim识别为字符串Reflect.apply第 2/3 参invalid(26)-(27)参数位置写错场景TS 类型标注invalid(38)-(46) 含string[]、readonly string[]、函数返回值getArray(): string[]类型标注 类型感知typeAware用例使用 TypeScript ESLint 的projectService见 test/no-uncalled-method.js#L7-L14对应的 valid 用例test/no-uncalled-method.js#L17-L83则系统性地覆盖了上节六类豁免以及unknown/arrays/arrayLike/value等无法静态判定类型的接收者——这些一律不报告正是「Unknown receivers are ignored to avoid false positives」策略的落地。小结no-uncalled-method的价值在于它精确刻画了一类「静默失败」的运行时 bug方法引用被赋给变量、放进条件或return。其设计取舍清晰可见于源码——检测面arrayMethods/stringMethods双白名单 × 多来源接收者识别求覆盖shouldSkip六类豁免 未知接收者忽略求克制二者共同保证 recommended 配置下开箱即用且误报极低。若你维护 TypeScript 项目开启该规则后可以直接消除const f array.sort这类低级失误而.call/.apply/bind、Reflect.apply、typeof检查与原型访问等合法引用模式均有明确豁免可在代码评审中放心采信其报告。延伸阅读规则官方文档、规则实现、测试用例、左值判定工具、TS 表达式解包工具。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考