
ESLint accessor-pairs 规则详解强制 getter 与 setter 成对出现【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint本指南以 ESLint 内置规则accessor-pairs为绝对核心系统讲解其设计动机、四个可配置选项setWithoutGet、getWithoutSet、enforceForClassMembers、enforceForTSTypes的完整语义与实战示例并结合本仓库的 规则实现源码 与 规则测试用例 深入剖析其底层检测原理。读完本文你将能够在自己的 ESLint 配置中精准启用并调优该规则避免 JavaScript 对象中只有 setter 没有 getter或反之这类难以排查的缺陷。为什么要强制访问器成对在 JavaScript 中为对象属性同时定义 getter 与 setter 是很常见的做法。accessor-pairs规则针对的场景是开发者只为一个属性定义了 setter写入访问器却始终没有定义对应的 getter读取访问器。由于缺少 getter该属性将永远无法被读取最终沦为一段无效代码——这正是本规则元信息中description所概括的Enforce getter and setter pairs in objects and classes强制对象与类中的 getter/setter 成对存在。以下是文档给出的最典型反例与正例// Bad const o { set a(value) { this.val value; } }; // Good const p { set a(value) { this.val value; }, get a() { return this.val; } };规则默认警告定义了 setter 却没有 getter的情况通过选项getWithoutSet它还会额外警告定义了 getter 却没有 setter的情况。该规则始终检查对象字面量object literals与属性描述符property descriptors默认情况下还同时检查类声明class declarations与类表达式class expressions。规则详解Rule Details该规则强制一种编码风格每个定义了 setter 的属性都必须配套一个 getter。启用getWithoutSet选项后规则进一步要求每个定义了 getter 的属性都必须配套一个 setter。规则检查的范围分为三类语法构造对象字面量ObjectExpression即{ get a() {}, set a(v) {} }形式属性描述符property descriptor即传给Object.defineProperty、Object.defineProperties、Object.create、Reflect.defineProperty的配置对象类成员ClassBody类的实例访问器与静态访问器默认启用检查可通过enforceForClassMembers关闭TypeScript 类型定义TSTypeLiteral、TSInterfaceBody接口或类型字面量中的访问器签名默认不检查可通过enforceForTSTypes开启。选项Options规则的完整选项如下JSON Schema 定义于 lib/rules/accessor-pairs.js#L115-L134所有选项均为布尔类型且不支持额外属性选项默认值作用setWithoutGettrue对有 setter 无 getter的属性发出警告getWithoutSetfalse对有 getter 无 setter的属性发出警告enforceForClassMemberstrue是否将规则应用于类的 getter/setter设为false后忽略类声明与类表达式enforceForTSTypesfalse是否将规则应用于 TypeScript 类型定义interface / type这四个默认值可以在源码的defaultOptions中直接确认lib/rules/accessor-pairs.js#L99-L106。若选项对象缺省某字段规则会取用对应默认值——例如只写{ getWithoutSet: true }则setWithoutGet仍保持默认的true。setWithoutGet默认开启以下代码在默认配置{ setWithoutGet: true }下均为错误incorrect/*eslint accessor-pairs: error*/ const q { set a(value) { this.val value; } }; const r {d: 1}; Object.defineProperty(r, c, { set: function(value) { this.val value; } });以下代码在默认配置{ setWithoutGet: true }下均为正确correct/*eslint accessor-pairs: error*/ const s { set a(value) { this.val value; }, get a() { return this.val; } }; const t {d: 1}; Object.defineProperty(t, c, { set: function(value) { this.val value; }, get: function() { return this.val; } });可以看到对象字面量中的set a(...)需要配套get a(...)属性描述符中的set需要配套get。两条路径缺一都会触发 Getter is not present 类别的报告。getWithoutSet开启该选项后规则开始强制getter 必须配 setter。以下代码在{ getWithoutSet: true }配置下均为错误/*eslint accessor-pairs: [error, { getWithoutSet: true }]*/ const u { set a(value) { this.val value; } }; const v { get a() { return this.val; } }; const w {d: 1}; Object.defineProperty(w, c, { set: function(value) { this.val value; } }); const x {d: 1}; Object.defineProperty(x, c, { get: function() { return this.val; } });注意在此配置下const u中只有 setter的对象同样是错误因为setWithoutGet仍为默认true——两个维度同时生效。以下代码在{ getWithoutSet: true }配置下均为正确/*eslint accessor-pairs: [error, { getWithoutSet: true }]*/ const y { set a(value) { this.val value; }, get a() { return this.val; } }; const z {d: 1}; Object.defineProperty(z, c, { set: function(value) { this.val value; }, get: function() { return this.val; } });enforceForClassMembers当enforceForClassMembers为true默认时getWithoutSet: true也会对类中有 getter 无 setter的成员发出警告setWithoutGet: true也会对类中有 setter 无 getter的成员发出警告。检查同时覆盖实例成员与static静态成员。以下代码在{ getWithoutSet: true, enforceForClassMembers: true }下均为错误/*eslint accessor-pairs: [error, { getWithoutSet: true, enforceForClassMembers: true }]*/ class Foo { get a() { return this.val; } } class Bar { static get a() { return this.val; } } const Baz class { get a() { return this.val; } static set a(value) { this.val value; } }其中Baz类的get a()实例与static set a()静态分属不同的成员集合各自都缺少配对因而两者都被报告。以下代码在{ setWithoutGet: true, enforceForClassMembers: true }下均为错误/*eslint accessor-pairs: [error, { setWithoutGet: true, enforceForClassMembers: true }]*/ class Foo { set a(value) { this.val value; } } const Bar class { static set a(value) { this.val value; } }当enforceForClassMembers设为false时规则完全忽略类。以下代码在{ getWithoutSet: true, setWithoutGet: true, enforceForClassMembers: false }下均为正确/*eslint accessor-pairs: [error, { getWithoutSet: true, setWithoutGet: true, enforceForClassMembers: false }]*/ class Foo { get a() { return this.val; } } class Bar { static set a(value) { this.val value; } } const Baz class { static get a() { return this.val; } } const Quux class { set a(value) { this.val value; } }enforceForTSTypes当enforceForTSTypes为true时getWithoutSet: true也会对 TypeScript 类型定义interface / type中有 getter 无 setter的访问器签名发出警告setWithoutGet: true也会对类型定义中有 setter 无 getter的访问器签名发出警告。以下 TypeScript 代码在{ getWithoutSet: true, enforceForTSTypes: true }下均为错误/*eslint accessor-pairs: [error, { getWithoutSet: true, enforceForTSTypes: true }]*/ interface I { get a(): string } type T { get a(): number }以下 TypeScript 代码在{ setWithoutGet: true, enforceForTSTypes: true }下均为错误/*eslint accessor-pairs: [error, { setWithoutGet: true, enforceForTSTypes: true }]*/ interface I { set a(value: unknown): void } type T { set a(value: unknown): void }当enforceForTSTypes为false时规则忽略 TypeScript 类型。以下代码在{ getWithoutSet: true, setWithoutGet: true, enforceForTSTypes: false }下均为正确/*eslint accessor-pairs: [error, { getWithoutSet: true, setWithoutGet: true, enforceForTSTypes: false }]*/ interface I { get a(): string } type T { set a(value: unknown): void }源码级原理剖析整体实现结构规则实现位于 lib/rules/accessor-pairs.jsmeta中定义了三组关键信息type: suggestion该规则属于建议性规则不参与eslint:recommended推荐集recommended: falsedefaultOptions四个选项的默认值见上文表格messages八条报告消息按缺失对象字面量 getter/setter缺失类 getter/setter缺失类型 getter/setter缺失属性描述符 getter/setter四种场景区分例如missingGetterInObjectLiteral会输出Getter is not present for {{ name }}.。核心算法按 key 归组后成对校验规则的核心逻辑集中在checkList函数lib/rules/accessor-pairs.js#L224-L271其思路可概括为三步提取 key通过astUtils.getStaticPropertyName(node)尝试静态解析属性名若为计算属性而无法静态求值则退化为保存该 key 节点对应的token 列表用 token 序列作为 key 的等价表示归组遍历节点列表用areEqualKeyslib/rules/accessor-pairs.js#L68-L79比较 key把同名的 getter 与 setter 归入同一个AccessorData分组静态名称直接按字符串比较token 列表则逐 token 比较类型与值areEqualTokenLists成对校验遍历所有分组——若启用了setWithoutGet且组内只有 setter 没有 getter则报告所有 setter若启用了getWithoutSet且组内只有 getter 没有 setter则报告所有 getter。值得注意的是类成员的检查按static与实例分开处理lib/rules/accessor-pairs.js#L331-L338这就是上面示例中class Baz的实例get a()与static set a()互不配对的原因。属性描述符的识别isPropertyDescriptor规则对对象字面量执行双重检查checkObjectExpressionlib/rules/accessor-pairs.js#L318-L323先按普通对象字面量检查再用astUtils.isPropertyDescriptor判断该对象是否作为属性描述符使用若是则检查其get/set字段。isPropertyDescriptor的实现位于 lib/rules/utils/ast-utils.js#L590-L637它通过isArgumentOfGlobalMethodCall识别以下全局调用场景Object.defineProperty(obj, key, descriptor)的第三个参数Reflect.defineProperty(obj, key, descriptor)的第三个参数Object.create(proto, descriptors)的第二个参数当参数对象自身嵌套在Property中时Object.defineProperties(obj, descriptors)的第一个参数同样为嵌套场景。只有被识别为属性描述符时才会用namesToCheck.has(get)/has(set)的方式检查描述符中是否成对出现get与set字段。报告位置与测试佐证报告时规则根据节点类型选择不同的消息模板与定位report函数lib/rules/accessor-pairs.js#L169-L203Property用missingGetterInObjectLiteralMethodDefinition用missingGetterInClassTSMethodSignature用missingGetterInType其余属性描述符用missingGetterInPropertyDescriptor。定位均指向函数头部astUtils.getFunctionHeadLoc便于开发者快速跳转。规则的完整行为在 tests/lib/rules/accessor-pairs.js3589 行中有系统验证覆盖了对象模式var { get: foo } bar不误报、无访问器的普通对象不误报、计算属性[get]普通键不误报、各选项开关组合、属性描述符、类成员、TypeScript 类型等场景。已知限制Known Limitations由于静态分析的天性规则无法考虑可能的副作用side effects在某些场景下不会报告缺失的访问器配对。场景一带副作用的计算属性键当 getter 与 setter 使用带副作用的计算键如[z]时规则无法在静态层面确认两次求值的键是否相同因而不会报告/*eslint accessor-pairs: error*/ const z 1; // no warnings const a { get [z]() { return this.val; }, set z { this.val value; } };场景二对象/类中存在重复键规则本身并不禁止对象字面量与类定义中的重复键当重复键导致 getter 与 setter 不再紧邻出现时规则可能漏报。下面的代码实际创建了只有 setter 的属性a但规则不会发出警告/*eslint accessor-pairs: error*/ // no warnings const b { get a() { return this.val; }, a: 1, set a(value) { this.val value; } };如果你还需要同时禁止对象字面量中的重复键可搭配 no-dupe-keys若需要禁止类定义中的重复成员名可搭配 no-dupe-class-members。这两条规则在本文档 front matter 的related_rules中亦有声明。何时不使用该规则When Not To Use It如果你并不关心对象上 getter 与 setter 是否同时存在——例如你的代码中有意使用只写属性write-only property作为内部数据通道——可以直接关闭该规则。此外由于规则默认检查类成员若项目风格允许类中独立定义 getter 或 setter也应显式将enforceForClassMembers设为false以规避不必要的告警。实用配置建议综合上文给出几个可直接投入使用的配置形态// 仅默认行为强制setter 必须有 getter不查类不查 TS 类型 { rules: { accessor-pairs: [error, { enforceForClassMembers: false }] } } // 双向强制getter 与 setter 必须成对覆盖对象、属性描述符与类 { rules: { accessor-pairs: [error, { setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }] } } // 全量强制在 TypeScript 项目中对 interface / type 的访问器签名同样生效 { rules: { accessor-pairs: [error, { setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true, enforceForTSTypes: true }] } }建议在启用前先了解团队现有代码中是否存在只读 getter或只写 setter的有意设计再决定是否开启getWithoutSet以避免在存量代码上引入大量噪音告警。【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考