eslint-plugin-unicorn 规则解析:no-duplicate-set-values —— 拦截 `Set` 构造器数组字面量中的重复值
2026/9/18 13:43:55 网站建设 项目流程

eslint-plugin-unicorn 规则解析:no-duplicate-set-values —— 拦截Set构造器数组字面量中的重复值

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

本篇技术指南聚焦 eslint-plugin-unicorn 中的no-duplicate-set-values规则,讲解它为何存在、如何判定重复、覆盖哪些边界情况、又刻意放过哪些场景,并结合规则源码、共享工具函数与测试用例,帮助你彻底理解这一建议型规则的判定模型,避免写出冗余的Set初始化代码。

规则定位:冗余即噪声,重复值在Set中毫无意义

Set的特性是成员唯一(基于 SameValueZero 语义去重),因此向new Set([...])传入的静态数组字面量中出现重复值,是纯粹的多余代码——运行结果不变,却增加阅读与维护成本。该规则正是为此而生,其完整描述见 docs/rules/no-duplicate-set-values.md。

规则默认开启于 ✅recommended配置,在 ☑️unopinionated配置中则处于关闭状态(见 readme.md 规则清单中该行标记)。规则的 meta 配置为type: 'suggestion'recommended: true,仅支持js/js语言,见 rules/no-duplicate-set-values.js。

判定范围:只盯new Set([...])数组字面量

规则入口在 rules/no-duplicate-set-values.js 的NewExpression监听器中,判定链路非常严格:

  1. 必须是new Set(...)恰好一个参数(通过isNewExpression辅助函数校验name: 'Set'argumentsLength: 1);
  2. 该参数的类型必须是ArrayExpression(数组字面量);
  3. 对数组元素调用共享工具getDuplicateArrayElements找出重复项并逐一上报。

因此以下写法一律不被检查

  • 没有newSet([1, 1])
  • Set名称的new NotSet([1, 1])
  • 限定名new globalThis.Set([1, 1])(源码只匹配裸标识符Set);
  • 非数组字面量参数,如new Set(foo)new Set(iterable)
  • 数组[...]Map、其他可迭代对象;
  • 链式set.add(1).add(1)调用。

这些排除项都在规则源码iterable.type !== 'ArrayExpression'的提前返回以及测试用例new Set(foo)Set([1, 1])new NotSet([1, 1])new globalThis.Set([1, 1])中得到印证(见 test/no-duplicate-set-values.js)。

重复判定模型:静态值比较 + 引用比较双通道

重复检测的核心实现在共享工具 rules/utils/get-duplicate-array-elements.js。对每个元素,规则先尝试提取无副作用的静态值,无法提取时退化为引用比较,两条通道互为补充:

  • 静态值通道:通过getStaticValueIfNoSideEffects(见 rules/utils/get-static-value.js)求值,仅当结果是可比较的原始值(nullstringnumberbooleanbigintundefined等,即isComparableStaticValue过滤掉objectfunction)时才参与比较;
  • 引用通道:当两侧都没有可比较静态值时,使用isSameReference(见 rules/utils/is-same-reference.js)判断两个表达式是否指向同一处引用。

静态值的比较采用SameValueZero语义(与Set内部去重语义一致):===判等,但两个NaN视为相等。这一语义由工具函数isSameValueZero显式实现。

示例一:字面量重复

// ❌ 报错:Remove duplicate value `'foo'` from the Set. const set = new Set([ 'foo', 'bar', 'foo', ]); // ✅ const set = new Set([ 'foo', 'bar', ]);

示例二:常量变量与静态表达式可被求值

// ❌ `foo` 的静态值就是 2 const foo = 2; const set = new Set([foo, 2]); // ✅ const foo = 2; const set = new Set([foo, 3]);

测试中还覆盖了二元表达式new Set([1 + 1, 2])、模板字符串与普通字符串new Set([foo, "foo"])、常量绑定const foo = 'bar'; new Set([foo, 'bar'])等静态求值场景,均被判为重复。

示例三:引用表达式重复

// ❌ 两个 foo.bar 是同一引用 const set = new Set([foo.bar, foo.bar]); // ✅ const set = new Set([foo.bar, foo.baz]);

引用通道还支持成员表达式的等价形式,例如new Set([foo["bar"], foo.bar])(点号与方括号写法等价)、new Set([this.foo, this.foo])都会被识别(见测试用例)。其实现细节在 rules/utils/is-same-reference.js:能解析出静态属性名时比较属性名与对象引用,否则比较computed标记、对象引用与属性引用。

值得注意的数值边界:NaN0/-0

因为静态值比较使用 SameValueZero,以下两组在===下结果不同的写法也会被正确标记:

// ❌ Set 内部会去重 NaN,故视为重复 new Set([NaN, NaN]); // ❌ Set 内部 0 与 -0 相等 new Set([0, -0]);

对应的测试用例new Set([NaN, NaN])new Set([0, -0])均位于 invalid 列表,验证了规则与Set运行时语义的一致性。

刻意放过的场景:副作用、对象字面量与展开符

规则文档明确声明"不建模元素之间的副作用或变更",从测试用例可以清晰看到边界设计:

  • 函数调用new Set([foo(), foo()])合法——两次调用可能有副作用,结果未必相同;
  • 对象/数组/正则字面量new Set([{}, {}])new Set([[], []])new Set([/foo/, /foo/])new Set([Symbol(), Symbol()])均合法——它们不是可比较的原始静态值,也不是同一引用;
  • 可能被 getter 变更的成员访问:测试用例用Object.definePropertyobject.value挂上 getter 后,new Set([object.value, 1])被判定为合法,这正是getStaticValueIfNoSideEffects对"潜在可变成员访问"与副作用求值进行防御的结果(见 rules/utils/get-static-value.js 中hasPotentiallyMutableMemberAccess与副作用检查逻辑);
  • 展开元素getDuplicateArrayElementsSpreadElement直接continue跳过,因此new Set([foo, ...bar, baz])合法;但展开前后的普通元素仍参与比较,new Set([foo, ...bar, foo])会被标记为重复(两个foo位于展开两侧,静态比较与引用比较不受展开元素影响)。

数组空洞(elision)也被视为重复

数组字面量中的空洞位在getDuplicateArrayElements中被统一当作undefined处理:new Set([, undefined])new Set([undefined, ,])new Set([,,])均为 invalid 用例。上报时若元素本身缺失(hole),规则会退而报告整个iterable节点,并输出值为'undefined'的占位文本——对应源码中element ?? iterableelement ? sourceCode.getText(element) : 'undefined'的两处兜底。

上报信息与自动修复

命中后规则输出唯一消息 IDno-duplicate-set-values,文案为:

Remove duplicate value{{value}}from the Set.

其中{{value}}取自重复元素的源码文本(如'foo'foo.bar)。需要说明的是,从 rules/no-duplicate-set-values.js 的create实现看,该规则不提供fix函数,属于仅报告、不自动修复的suggestion类型规则——重复值由谁保留、删除哪个,需要开发者结合语义自行决定。

测试覆盖与回归保障

规则的完整行为由 test/no-duplicate-set-values.js 的快照测试锁定,invalid 列表覆盖了:字面量重复(数字、字符串、nullundefined、负号字面量-1)、SameValueZero 边界(NaN0/-0)、多重重复([1, 1, 2, 2])、静态求值(1 + 1、模板字符串、常量变量)、引用重复(标识符、成员表达式、计算属性、this成员)、空洞位、跨展开元素重复,以及const foo = {}; new Set([foo, foo])这类纯对象引用重复。valid 列表则覆盖了类型严格区分(new Set([1, 2, "1"])合法,数字与字符串不等价)、副作用调用、getter 防御、对象字面量等放行场景。

若希望在你的项目中启用该规则,可在 ESLint flat config 中引入 eslint-plugin-unicorn 的recommended配置(默认开启),或在自定义规则项中显式配置:

export default [ // ... { rules: { 'unicorn/no-duplicate-set-values': 'error', }, }, ];

小结

no-duplicate-set-values是一个小而精准的建议型规则:它只审查new Set([...])数组字面量,用"无副作用静态值(SameValueZero)+ 引用等价"的双通道模型识别冗余元素,同时严谨地回避函数调用、getter、对象字面量与展开元素等不确定场景。理解它的判定边界,既能放心开启该规则,也能在遇到误报时准确判断是否属于规则刻意放行的合法情况。

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

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

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

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

立即咨询