eslint-plugin-unicorn `prefer-at` 规则实战:用 `.at()` 统一数组与字符串的索引访问
2026/9/18 12:55:11 网站建设 项目流程

eslint-plugin-unicornprefer-at规则实战:用.at()统一数组与字符串的索引访问

【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn

本篇技术指南围绕 eslint-plugin-unicorn(一个提供 300+ 条强大 ESLint 规则的插件)中的prefer-at规则展开,系统讲解它如何将数组、字符串上的负索引访问、charAt()、单字符substring()以及「取最后一个元素」等反模式统一改写为语义更清晰的.at()。读完本文,你将掌握该规则默认检查的全部模式、两个核心配置项(checkAllIndexAccessgetLastElementFunctions)的用法、安全例外规则,以及规则底层 AST 修复逻辑的源码实现。

规则概览:它在检查什么

prefer-at规则(规则文档、规则源码)的核心目标是:凡是"通过索引取值"的写法,优先改用 ES2022 的.at()方法,包括:

  • Array#at()用于数组索引访问;
  • String#at()用于字符串索引访问;
  • TypedArray#at()用于类型化数组;
  • String#charAt()的替换场景。

规则类型为suggestion,同时支持--fix自动修复meta.fixable: 'code')和编辑器手动建议修复hasSuggestions: true),见 rules/prefer-at.js。在插件索引文件 rules/index.js 中以prefer-at名称导出。

该规则默认在插件的recommendedunopinionated两套配置中启用,meta.docs.recommended标记为'unopinionated'。这意味着它只会在与既有代码风格冲突最小的情况下触发(默认仅检查负索引),适合直接加入团队的推荐配置。

默认检查的 5 类反模式

1. 数组负索引访问:array[array.length - n]

这是最常见的写法。默认规则会检查形如xxx[xxx.length - n](n 为正数)的负索引访问,并自动重写为.at(-n)

// ❌ const foo = array[array.length - 1]; const foo = array[array.length - 5]; // ✅ const foo = array.at(-1); const foo = array.at(-5);

从源码看,该逻辑监听MemberExpression节点(rules/prefer-at.js),通过共享工具 getNegativeIndexLengthNode 识别length - n结构。该工具只接受BinaryExpressionoperator === '-'、右侧为字面量正数,并要求左侧确实是对应对象的length(通过isLengthOf校验);它还支持嵌套的BinaryExpression(如array.length - 1 - 1),对括号包裹((( array.length )) - 1)也能处理。

自动修复时,规则会先移除length节点(removeLengthNode),再把[替换为.at(、把]替换为),同时处理可选链(array?.[array.length - 1]会修复为array?.at(-1))以及foo[foo.length - 1]中多余空格。

2.String#charAt()负索引:string.charAt(string.length - n)

// ❌ const foo = string.charAt(string.length - 5); // ✅ const foo = string.at(-5);

规则监听CallExpression(rules/prefer-at.js),只匹配方法名为charAt、恰好一个参数、且非可选调用的调用。当参数是length - n结构时报告string-char-at-negative并给出建议修复(将charAt替换为at、移除length节点)。

3.String#substring()取单字符:string.substring(index, index + 1)

// ❌ const foo = string.substring(index, index + 1); // ✅ const foo = string.at(index);

这是该规则最有特色的检测项,通过共享工具 getSubstringSingleCharacterIndex 识别"取单个字符"的substring()调用:

  • 两个参数都是非负整数字面量且差值为 1:substring(0, 1)substring(1, 2)substring(2, 1)(注意substring会自动交换参数,因此顺序无关);
  • 形如substring(index, index + 1)substring(index, 1 + index)
  • 形如substring(index - 1, index)

若替换区间内存在注释(如string.substring(index, /* comment */ index + 1)),规则会照常报告,但降级为不包含 fix 的纯建议(见 rules/prefer-at.js),避免误删注释。

4.slice()取首元素:array.slice(-1)[0]/.pop()/.shift()

// ❌ const foo = array.slice(-1)[0]; const foo = array.slice(-1).pop(); const foo = array.slice(-1).shift(); // ✅ const foo = array.at(-1);

规则对slice(-1)之后紧跟[0].shift().pop()的"取首元素"模式进行修复(rules/prefer-at.js)。核心判定函数 getSliceCallResult 要求:

  • slice的第一个参数是负整数字面量;
  • slice(-1, -8)这类两参数形式仅当endIndex === startIndex + 1时才安全修复;
  • 部分场景(如array.slice(-9).shift()这类参数不满足严格条件的情况)只给出建议修复而非自动修复,以规避sliceat对越界行为不一致(如shift()返回undefined.at()也会返回undefined,但某些边界参数下语义存在差异)的风险。

当模式位于赋值左侧(LHS,如array.slice(-1)[0] = 1)时规则会跳过,避免把可写位置改成只读的方法调用。

5. "取最后一个元素"的函数调用:_.last()

// ❌ const foo = lodash.last(array); // ✅ const foo = array.at(-1);

_.last()lodash.last()underscore.last()三个函数始终被检查(硬编码在 lodashLastFunctions),报告get-last-function并自动修复为.at(-1)。修复时规则会妥善处理括号(例如_.last(new Array)会修复为(new Array).at(-1))以及分号问题(当上一行语句未加分号时,会在替换结果前补;,见 rules/prefer-at.js)。

规则刻意放过的安全例外

prefer-at的设计哲学是宁可不查,也不误报,源码与测试(test/prefer-at.js)都覆盖了大量例外场景:

// ✅ 正索引访问默认不检查(除非开启 checkAllIndexAccess) const foo = array[100]; // ✅ 赋值左侧不检查(.at() 只读,不可作为赋值目标) array[array.length - 1] = foo; // ✅ DOM 集合不检查:children / childNodes / querySelectorAll() 等 // 浏览器中这些集合尚不能保证支持 .at() const foo = element.children[element.children.length - 1]; // ✅ arguments 不检查:类数组对象没有 Array#at() function foo() { return arguments[arguments.length - 1]; }

DOM 例外通过 isDomCollectionReceiver 实现:childNodeschildren属性以及getElementsByClassNamequerySelectorAll等方法会被忽略(源码注释表明,待浏览器 NodeList/HTMLCollection 稳定支持.at()后可移除)。同理,对象字面量、非字符串字面量、箭头函数、普通函数、类表达式等"明显不是数组"的接收者也一律跳过(isObviouslyNonArrayReceiver)。

此外,prefer-at与同为 unicorn 的prefer-negative-index规则形成互补:像array.at(array.length - 1)这种写法本规则不处理,但 prefer-negative-index 会将其改写为array.at(-1)

配置项详解

checkAllIndexAccess

  • 类型:boolean
  • 默认值:false

默认只检查负索引;设为true后,非负整数字面量索引也会被检查并改写为.at()

{ 'unicorn/prefer-at': [ 'error', { checkAllIndexAccess: true } ] }
/* eslint unicorn/prefer-at: ["error", {"checkAllIndexAccess": true}] */ const foo = bar[10]; // Fails, will fix to `bar.at(10)` const foo = bar[unknownProperty]; // Passes const foo = string.charAt(unknownIndex); // Fails

开启后,规则通过 getStaticValueIfNoSideEffects 静态求值索引,只接受无副作用的、可静态确定的安全非负整数(见 rules/prefer-at.js);charAt()则不关心索引具体值,只要调用形态匹配就会检查(测试 test/prefer-at.js 中string.charAt(9)string.charAt(unknown)string.charAt(1.5)均为 invalid)。注意let声明的绑定不会被解析为"确定的对象",因此let object = {1: 1, a: 2}; object[1]仍会被报告(测试注释明确说明了这一点)。

getLastElementFunctions

  • 类型:string[]

用于登记自定义的"取最后一个元素"函数,支持点路径写法(utils.lastElement):

{ 'unicorn/prefer-at': [ 'error', { getLastElementFunctions: [ 'getLast', 'utils.lastElement' ] } ] }
/* eslint unicorn/prefer-at: ["error", {"getLastElementFunctions": ["utils.lastElement"]}] */ // ❌ const foo = utils.lastElement(bar); // ✅ const foo = bar.at(-1);

schema 中要求该数组元素唯一(uniqueItems: true),且规则在匹配时通过 isNodeMatchesNameOrPath 支持任意层级的属性路径与大小写精确匹配(测试中甚至验证了' utils.lastOne '这种带空白的配置也能被正确 trim 后匹配,见 test/prefer-at.js)。

规则之间的协作关系

  • unicorn/prefer-string-slice互补prefer-at处理"单字符"的substring()模式,而prefer-string-slice负责一般性的字符串切片场景,两者覆盖范围不重叠(见 prefer-string-slice 文档)。
  • unicorn/prefer-negative-index互补array.at(array.length - 1)这类"已用.at()但仍是负索引表达式"的写法由 prefer-negative-index 规则 继续优化为array.at(-1)

源码级的检测与修复流程

整条规则的执行可以概括为 5 个独立的CallExpression/MemberExpression监听分支(rules/prefer-at.js),每条分支对应一组messageId与修复策略:

场景messageId修复方式
数组负索引访问negative-index自动 fix
数组非负索引(开启选项后)index自动 fix
charAt(length - n)string-char-at-negative建议修复
charAt(...)普通调用(开启选项后)string-char-at建议修复
substring单字符string-substring建议修复(区间含注释时无 fix)
slice(-1)[0]/pop()/shift()slice满足安全条件自动 fix,否则建议
取最后元素的函数调用get-last-function自动 fix

规则的defaultOptions[{getLastElementFunctions: [], checkAllIndexAccess: false}],与文档默认值保持一致(rules/prefer-at.js);配置 schema 禁止额外未知属性(additionalProperties: false)。

对于.at()的浏览器与运行环境兼容性,ES2022 已将其加入标准库,Node.js 16.6+ 及现代浏览器均支持;团队在引入该规则前,建议根据目标运行环境确认.at()可用(或依赖 polyfill),这也是插件在 DOM 集合与arguments场景主动避让的原因所在。

【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询