ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

eslint-plugin-unicorn 的 class-reference-in-static-methods 规则:从 AVA 快照读懂静态方法类引用风格

eslint-plugin-unicorn 的 class-reference-in-static-methods 规则:从 AVA 快照读懂静态方法类引用风格 eslint-plugin-unicorn 的 class-reference-in-static-methods 规则从 AVA 快照读懂静态方法类引用风格【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicornclass-reference-in-static-methods是 eslint-plugin-unicorn 中用于统一静态方法内类引用风格的规则要么统一用this/super做动态静态分发dynamic static dispatch要么统一用类名做直接引用。本文以该规则在 test/snapshots/class-reference-in-static-methods.js.md 中的 AVA 快照为核心证据结合 官方规则文档 与 规则源码完整还原该规则在默认配置、类名模式、混合选项与 TypeScript 场景下的所有报错消息、编辑器建议suggestion与边界行为。读完本文你将能准确预测这条规则在任何代码形态下的检查结果并能在自己的项目中正确配置与使用它。一、规则要解决的风格分歧在 JavaScript 类中静态方法内部引用当前类或父类的静态成员有两种等价写法// 风格 A动态静态分发dynamic static dispatch class Foo extends Bar { static baz() { this.qux(); // 跟随子类分发 super.qux(); // 直接调用父类实现 } } // 风格 B类名直接引用 class Foo extends Bar { static baz() { Foo.qux(); // 绑定在当前类上 Bar.qux(); // 直接指向父类 } }两种风格语义并不完全相同this/super会跟随继承链动态分发而类名引用则将调用硬绑定到具体类。混用会让读者无法判断一个静态方法究竟是有意跟随子类分发、还是有意绑定到特定类。该规则的作用就是强制全项目统一其中一种风格默认偏好this与superdocs/rules/class-reference-in-static-methods.md。从 readme 的规则总表可以看到该规则的属性它被标记为✅在recommended配置中启用unopinionated配置中不启用、通过编辑器建议手动修复非--fix自动修复readme.md。二、默认行为偏好 this 与 super默认配置{preferThis: true, preferSuper: true}下规则要求静态方法内使用this代替当前类名、使用super代替父类名。快照文件的第一组 invalid 用例完整展示了这一行为test/snapshots/class-reference-in-static-methods.js.md输入代码报错消息编辑器建议class A {static foo() {return A.foo();}}Use this instead of A in static methods.A.foo()→this.foo()class A {static foo() {return A;}}Use this instead of A in static methods.A→thisclass A extends B {static foo() {return B.foo();}}Use super instead of B in static methods.B.foo()→super.foo()class A extends B {static foo() {return B[foo]();}}Use super instead of B in static methods.B[foo]()→super[foo]()class A extends B {static foo() {return () B.foo();}}Use super instead of B in static methods.() B.foo()→() super.foo()官方文档中给出的错误/正确对比示例与此一致docs/rules/class-reference-in-static-methods.md// ❌ 类名引用 class Foo extends Bar { static baz() { Foo.qux(); Bar.qux(); } } // ✅ this / super 动态分发 class Foo extends Bar { static baz() { this.qux(); super.qux(); } }快照还覆盖了类表达式class expression场景const A class { static foo() { return A.foo(); } };匿名类表达式赋值给变量A时A视为当前类引用建议替换为thisconst A class B { static foo() { return B.foo(); } };具名类表达式的内部名B同样被识别为当前类引用建议替换为this。注意箭头函数这一行() B.foo()位于静态方法体内但跨了一层箭头函数仍会被检查并建议替换为super——说明箭头函数不会打断“静态方法”的判定边界源码中getStaticMethod向上遍历时会continue跳过ArrowFunctionExpression见 rules/class-reference-in-static-methods.js。三、配置项preferThis 与 preferSuper规则接受一个对象类型的选项docs/rules/class-reference-in-static-methods.md选项类型默认值作用preferThisbooleantrue静态方法内偏好用this而非当前类名preferSuperbooleantrue静态方法内偏好用super而非父类名两个选项相互独立可任意组合。对应的 JSON Schema 在源码中定义且不允许额外属性additionalProperties: falserules/class-reference-in-static-methods.js。3.1 类名模式{preferThis: false, preferSuper: false}将两个选项都关闭后规则方向完全反转静态方法内必须使用类名引用this/super反而成为违规。快照第二组 invalid 用例同样位于 test/snapshots/class-reference-in-static-methods.js.md输入代码报错消息编辑器建议class A {static foo() {return this.foo();}}Use A instead of this in static methods.this.foo()→A.foo()class A {static foo() {return this;}}Use A instead of this in static methods.this→Aclass A extends B {static foo() {return super.foo();}}Use B instead of super in static methods.super.foo()→B.foo()class A extends B {static foo() {return () super.foo();}}Use B instead of super in static methods.() super.foo()→() B.foo()const A class {static foo() {return this.foo();}};Use A instead of this in static methods.this.foo()→A.foo()官方文档中的类名模式示例docs/rules/class-reference-in-static-methods.md// 配置 {preferThis: false, preferSuper: false} 时 // ❌ class Foo extends Bar { static baz() { this.qux(); super.qux(); } } // ✅ class Foo extends Bar { static baz() { Foo.qux(); Bar.qux(); } }3.2 名称被遮蔽时报错但不提供建议当类名或父类名在静态方法内被局部变量遮蔽时替换会导致语义变化因此规则只报告、不给出建议。快照中的两个典型用例class A {static foo() {const A other; return this.foo();}}→ 消息Use the class name instead of \this in static methods.无建议class A extends B {static foo() {const B other; return super.foo();}}→ 消息Use the superclass name instead of \super in static methods.无建议。对应测试文件中的注释也明确标注了这一点“Class/superclass name shadowed: report but dont suggest a behavior-changing replacement”test/class-reference-in-static-methods.js。源码通过findVariable做作用域解析isReferenceNameAvailable、isSameVariable来判断名称是否可安全引用rules/class-reference-in-static-methods.js。3.3 匿名类与非常规父类同样无法给出建议以下两种情况下类名/父类名无法解析为简单标识符同样只报错不修复export default class {static foo() {return this.foo();}}默认导出的匿名类没有名字可用消息为Use the class name instead of \this in static methods.class A extends mixin(B) {static foo() {return super.foo();}}父类是调用表达式mixin(B)而非简单标识符消息为Use the superclass name instead of \super in static methods.。源码中getClassReferenceNode只处理类声明名与VariableDeclarator初始化的匿名类表达式rules/class-reference-in-static-methods.jsgetSimpleSuperClassReferenceNode要求superClass必须是Identifier类型rules/class-reference-in-static-methods.js。3.4 两个选项独立开启同一表达式可能同时报两条错误快照第三组用例验证了选项的独立性test/snapshots/class-reference-in-static-methods.js.md配置{preferThis: false}时class A extends B {static foo() {return this.foo() B.foo();}}会产生Error 1/2this→A与Error 2/2B→super两条独立错误两条建议互不冲突配置{preferSuper: false}时class A extends B {static foo() {return A.foo() super.foo();}}同样产生两条错误A→this与super→B。这证明preferThis只控制“当前类名 vs this”的偏好preferSuper只控制“父类名 vs super”的偏好二者正交。四、完整消息体系与建议机制源码中定义了 6 个报告消息 ID 与 1 个建议消息 IDrules/class-reference-in-static-methods.js消息 ID消息文本触发场景thisUse this instead of {{name}} in static methods.默认模式类名应改为thissuperUse super instead of {{name}} in static methods.默认模式父类名应改为superclassUse {{name}} instead of this in static methods.类名模式this应改为类名super-classUse {{name}} instead of super in static methods.类名模式super应改为父类名class-unavailableUse the class name instead of this in static methods.类名不可用匿名类/被遮蔽super-class-unavailableUse the superclass name instead of super in static methods.父类名不可用非简单标识符/被遮蔽suggestionReplace {{source}} with {{replacement}}.编辑器建议的替换说明规则元数据中hasSuggestions: truerules/class-reference-in-static-methods.js意味着这些修复不是--fix自动修复而是通过 IDE 的“快速修复”Quick Fix / editor suggestion手动触发——这正是 readme 规则表中该规则只有 没有 的原因。快照中每条错误下方的Suggestion 1/1: Replace \A with this. 就是建议消息在测试输出中的呈现形式。五、源码实现要点边界情况的判定快照中大量 “valid” 用例被测试认定为合法揭示了规则的边界判定逻辑。虽然快照 markdown 只记录 invalid 用例但对应的 test/class-reference-in-static-methods.js 中的 valid 列表完整刻画了这些规则赋值目标不报告A.foo 1、A.foo.bar 1、A.foo、delete A.foo、for (A.foo in object)、解构赋值({foo: A.foo} object)等场景下类名属于赋值/删除目标替换为this没有意义一律跳过。源码中isAssignmentTarget向上遍历赋值表达式、更新表达式、delete一元表达式与for-in/for-of左值rules/class-reference-in-static-methods.js。直接调用/构造/标签模板不报告A()、new A()、Afoo不会被替换避免把直接调用改成this()改变语义反过来类名模式下this()、new this()、thisfoo也是合法用例。判定依据是isDirectCalleerules/class-reference-in-static-methods.js。私有成员访问不报告A.#foo与#foo in A私有品牌检查不触发检查对应isPrivateMemberAccess与isPrivateBrandCheckrules/class-reference-in-static-methods.js。非静态方法不受影响class A {foo() {return A.foo();}}合法——规则只作用于静态上下文。静态字段与静态块class A {static foo A.foo;}与class A {static {A.foo();}}同样合法。源码中静态方法边界类型集合STATIC_METHOD_BOUNDARY_TYPES包含PropertyDefinition、AccessorProperty、StaticBlock与ClassBodyrules/class-reference-in-static-methods.js规则只针对MethodDefinition且static true的方法体rules/class-reference-in-static-methods.js。普通函数会打断判定class A {static foo() {return function () {return A.foo();};}}合法——普通函数表达式非箭头函数内部不再视为静态方法上下文。括号包裹不替换(B).foo()、(B)[foo]()合法因为源码对父类名引用要求“简单成员访问”且isParenthesized时跳过rules/class-reference-in-static-methods.js。局部同名变量不算类引用class A {static foo() {const A other; return A.foo();}}合法——A解析为局部变量而非类。六、TypeScript 场景类型位置的名称不受影响快照最后一部分使用 TypeScript 解析器测试中通过parsers.typescript指定见 test/class-reference-in-static-methods.js验证了规则对 TS 语法的处理输入代码报错消息建议class A {static foo(): unknown {return A.foo();}}Use this instead of A in static methods.A.foo()→this.foo()class A {static foo(): A {return A.foo();}}Use this instead of A in static methods.A.foo()→this.foo()class A extends B {static foo(): unknown {return B.foo();}}Use super instead of B in static methods.B.foo()→super.foo()关键在于返回类型注解(): A中的A处于类型位置不会被当作类引用报告同时class A {static foo(): A {return A.foo();}}中方法体里的A.foo()依然会被正确识别并建议改为this.foo()。源码通过isTypeScriptTypeIdentifier排除TSTypeReference与TSTypeQuery中的标识符rules/class-reference-in-static-methods.js。此外测试 valid 用例还覆盖了各类 TS 表达式包装器——(A as typeof A)()、A!()、(A satisfies typeof A)()、(typeof AA)()等场景均被视为合法直接调用对应源码中的TYPESCRIPT_EXPRESSION_WRAPPER_TYPES集合TSAsExpression、TSInstantiationExpression、TSNonNullExpression、TSSatisfiesExpression、TSTypeAssertionrules/class-reference-in-static-methods.js。七、如何在项目中启用由于该规则在recommended配置中默认启用最简单的接入方式是直接扩展 recommended 配置。以 flat config 为例参考 readme.md 的用法import unicorn from eslint-plugin-unicorn; export default [ { plugins: { unicorn, }, extends: [ unicorn/recommended, ], }, ];如果需要切换为类名引用风格可在rules中显式覆盖{ rules: { unicorn/class-reference-in-static-methods: [error, { preferThis: false, preferSuper: false, }], }, }规则的测试通过 AVA 的 snapshot 断言来锁定行为test/class-reference-in-static-methods.js快照报告文件由 AVA 自动生成是验证规则实际输出消息文本、建议内容、选项组合效果最直接的证据源。八、小结class-reference-in-static-methods解决的是静态方法内一种容易被忽视的风格一致性问题this/super的动态分发与类名直接引用语义不同、各有用武之地但混用会让代码意图模糊。通过preferThis与preferSuper两个正交选项它可以双向工作并在类名不可用、名称被遮蔽、赋值目标、私有成员访问、TypeScript 类型位置等边界场景下谨慎地只报错、不提供有风险的自动修复。理解 AVA 快照中每一条用例就等于完整掌握了这条规则的判定模型这在迁移代码库、审查规则冲突或自定义配置时都能提供精确的预期。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进