class-methods-use-this
强制类方法使用 this
类 通常用于将可重用的逻辑,特别是有状态的逻辑,封装到对象中,其中每个实例的状态通过 this 访问。当 API 使用实例方法编写时,它向使用者传达了以下信息:
-
该方法的结果与其被调用的对象相关,包括可能的对象状态。
const array1 = [1, 2, 3]; const array2 = [4, 5, 6]; // Using the `includes()` method on different objects gives different results: array1.includes(1); // true array2.includes(1); // false // Modifying the state of an object may change the outcome of its instance methods: array2.push(1); array2.includes(1); // true -
这个方法在没有相关对象的情况下使用是没有意义的。(例如,在没有要操作的数组的情况下调用
Array#includes()是没有意义的。)
可以有一个不使用 this 的类方法,例如:
🌐 It’s possible to have a class method which doesn’t use this, such as:
class Person {
sayHi() {
console.log("Hi!");
}
}
const person = new Person();
person.sayHi(); // => "Hi!"
如果一个类的实例方法不使用 this,通常意味着它不访问任何实例状态,因此不需要成为一个方法。
因此,它有时可以被重构为一个普通函数或一个静态方法,这可能更好地向 API 的使用者传达意图。
🌐 If a class instance method does not use this, that normally means that it does not access any instance state and therefore doesn’t need to be a method.
Therefore, it can sometimes be refactored into an ordinary function or a static method, which may better communicate intent to users of the API.
在上面的例子中,sayHi 方法没有使用 this,所以我们可以把它做成一个普通函数:
🌐 In the example above, the sayHi method doesn’t use this, so we can make it an ordinary function:
// Ordinary function
function sayHi() {
console.log("Hi!");
}
// No need for `Person` class or any instance thereof
sayHi(); // => "Hi!"
// Alternately, a static method may be used if it offers a more natural API
class Person {
static sayHi() {
console.log("Hi!");
}
}
Person.sayHi(); // => "Hi!"
// Keep in mind that, either way, the following now throws an error,
// since sayHi() is no longer an instance method!
//
// const person = new Person();
// person.sayHi();
也有可能作者忘记使用他们本打算包含的某些实例数据。
🌐 It’s also possible the author forgot to use some piece of instance data that they intended to include.
class Person {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`Hi from ${this.name}!`);
}
}
const alice = new Person('Alice');
alice.sayHi(); // => 'Hi from Alice!'
const bob = new Person('Bob');
bob.sayHi(); // => 'Hi from Bob!'
规则详情
🌐 Rule Details
此规则标记未使用 this 的类实例方法。
🌐 This rule flags class instance methods that do not use this.
此规则的错误代码示例:
🌐 Examples of incorrect code for this rule:
/*eslint class-methods-use-this: "error"*/
class A {
foo() {
console.log("Hello World"); /* error Expected 'this' to be used by class method 'foo'. */
}
}
符合此规则的正确代码示例:
🌐 Examples of correct code for this rule:
/*eslint class-methods-use-this: "error"*/
class A {
foo() {
this.bar = "Hello World"; // OK, `this` is used
}
}
class B {
constructor() {
// OK. constructor is exempt
}
}
class C {
static foo() {
// OK. static methods aren't expected to use this.
}
static {
// OK. static blocks are exempt.
}
}
选项
🌐 Options
此规则有四个选项:
🌐 This rule has four options:
"exceptMethods"允许使用此规则忽略指定的方法名称。"enforceForClassFields"强制要求作为实例字段初始化器使用的箭头函数和函数表达式使用this。这同样适用于自动访问器字段(使用accessor关键字声明的字段),这些字段是 阶段 3 装饰器提案 的一部分。(默认值:true)"ignoreOverrideMethods"会忽略标记了override修饰符 的成员。(仅限 TypeScript,默认值:false)"ignoreClassesWithImplements"会忽略在实现了implements接口的类中定义的类成员。(仅限 TypeScript)
exceptMethods
"class-methods-use-this": [<enabled>, { "exceptMethods": [<...exceptions>] }]
"exceptMethods"选项允许你传递一个方法名称数组,用于指定你希望忽略警告的方法。例如,你可能有一个来自外部库的规范,它要求你将某个方法覆盖为普通函数(而不是静态方法),并且在函数体内没有使用this。在这种情况下,你可以将该方法添加到需要忽略警告的方法列表中。
🌐 The "exceptMethods" option allows you to pass an array of method names for which you would like to ignore warnings. For example, you might have a spec from an external library that requires you to overwrite a method as a regular function (and not as a static method) and does not use this inside the function body. In this case, you can add that method to ignore in the warnings.
在未使用 "exceptMethods" 的情况下,此规则的错误代码示例:
🌐 Examples of incorrect code for this rule when used without "exceptMethods":
/*eslint class-methods-use-this: "error"*/
class A {
foo() {
}
}
当与 exceptMethods 一起使用时,此规则的 正确 代码示例:
🌐 Examples of correct code for this rule when used with exceptMethods:
/*eslint class-methods-use-this: ["error", { "exceptMethods": ["foo", "#bar"] }] */
class A {
foo() {
}
#bar() {
}
}
enforceForClassFields
"class-methods-use-this": [<enabled>, { "enforceForClassFields": true | false }]
enforceForClassFields 选项强制使用箭头函数和作为实例字段初始化器的函数表达式时使用 this。这也适用于自动访问器字段(使用 accessor 关键字声明的字段),它们是 阶段 3 装饰器提案 的一部分。(默认值:true)
🌐 The enforceForClassFields option enforces that arrow functions and function expressions used as instance field initializers utilize this. This also applies to auto-accessor fields (fields declared with the accessor keyword) which are part of the stage 3 decorators proposal. (default: true)
使用 { "enforceForClassFields": true } 选项(默认)时,该规则的错误代码示例:
🌐 Examples of incorrect code for this rule with the { "enforceForClassFields": true } option (default):
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
class A {
foo = () => {}
}
使用 { "enforceForClassFields": true } 选项(默认)时符合此规则的正确代码示例:
🌐 Examples of correct code for this rule with the { "enforceForClassFields": true } option (default):
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
class A {
foo = () => {this;}
}
使用 { "enforceForClassFields": false } 选项时,此规则的正确代码示例:
🌐 Examples of correct code for this rule with the { "enforceForClassFields": false } option:
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": false }] */
class A {
foo = () => {}
}
使用 { "enforceForClassFields": true } 选项(默认)时,此规则的 错误 TypeScript 代码示例:
🌐 Examples of incorrect TypeScript code for this rule with the { "enforceForClassFields": true } option (default):
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
class A {
foo = () => {}
accessor bar = () => {}
}
使用 { "enforceForClassFields": true } 选项(默认)针对此规则的 正确 TypeScript 代码示例:
🌐 Examples of correct TypeScript code for this rule with the { "enforceForClassFields": true } option (default):
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
class A {
foo = () => {this;}
accessor bar = () => {this;}
}
使用 { "enforceForClassFields": false } 选项时,该规则的 正确 TypeScript 代码示例:
🌐 Examples of correct TypeScript code for this rule with the { "enforceForClassFields": false } option:
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": false }] */
class A {
foo = () => {}
accessor bar = () => {}
}
ignoreOverrideMethods
"class-methods-use-this": [<enabled>, { "ignoreOverrideMethods": true | false }]
ignoreOverrideMethods 选项会忽略标记有 override 修饰符的成员。(默认值:false)
🌐 The ignoreOverrideMethods option ignores members that are marked with the override modifier. (default: false)
使用 { "ignoreOverrideMethods": false } 选项(默认)时,此规则的 错误 TypeScript 代码示例:
🌐 Examples of incorrect TypeScript code for this rule with the { "ignoreOverrideMethods": false } option (default):
/*eslint class-methods-use-this: ["error", { "ignoreOverrideMethods": false }] */
abstract class Base {
abstract method(): void;
abstract property: () => void;
}
class Derived extends Base {
override method() {}
override property = () => {};
}
使用 { "ignoreOverrideMethods": false } 选项(默认)针对此规则的 正确 TypeScript 代码示例:
🌐 Examples of correct TypeScript code for this rule with the { "ignoreOverrideMethods": false } option (default):
/*eslint class-methods-use-this: ["error", { "ignoreOverrideMethods": false }] */
abstract class Base {
abstract method(): void;
abstract property: () => void;
}
class Derived extends Base {
override method() {
this.foo = "Hello World";
};
override property = () => {
this;
};
}
使用 { "ignoreOverrideMethods": true } 选项遵守此规则的 正确 TypeScript 代码示例:
🌐 Examples of correct TypeScript code for this rule with the { "ignoreOverrideMethods": true } option:
/*eslint class-methods-use-this: ["error", { "ignoreOverrideMethods": true }] */
abstract class Base {
abstract method(): void;
abstract property: () => void;
}
class Derived extends Base {
override method() {}
override property = () => {};
}
ignoreClassesWithImplements
"class-methods-use-this": [<enabled>, { "ignoreClassesWithImplements": "all" | "public-fields" }]
ignoreClassesWithImplements 会忽略在 implements 接口中定义的类成员。该选项接受两个可能的值:
🌐 The ignoreClassesWithImplements ignores class members that are defined within a class that implements an interface. The option accepts two possible values:
"all"- 忽略所有实现接口的类"public-fields"- 仅忽略实现接口的类中的公共字段
使用 { "ignoreClassesWithImplements": "all" } 的此规则的 不正确 TypeScript 代码示例:
🌐 Examples of incorrect TypeScript code for this rule with the { "ignoreClassesWithImplements": "all" }:
/*eslint class-methods-use-this: ["error", { "ignoreClassesWithImplements": "all" }] */
class Standalone {
method() {}
property = () => {};
}
使用 { "ignoreClassesWithImplements": "all" } 选项时,该规则的 正确 TypeScript 代码示例:
🌐 Examples of correct TypeScript code for this rule with the { "ignoreClassesWithImplements": "all" } option:
/*eslint class-methods-use-this: ["error", { "ignoreClassesWithImplements": "all" }] */
interface Base {
method(): void;
}
class Derived implements Base {
method() {}
property = () => {};
}
使用 { "ignoreClassesWithImplements": "public-fields" } 选项时,该规则的 错误 TypeScript 代码示例:
🌐 Examples of incorrect TypeScript code for this rule with the { "ignoreClassesWithImplements": "public-fields" } option:
/*eslint class-methods-use-this: ["error", { "ignoreClassesWithImplements": "public-fields" }] */
interface Base {
method(): void;
}
class Derived implements Base {
method() {}
property = () => {};
private privateMethod() {}
private privateProperty = () => {};
protected protectedMethod() {}
protected protectedProperty = () => {};
}
使用 { "ignoreClassesWithImplements": "public-fields" } 选项时,该规则的 正确 TypeScript 代码示例:
🌐 Examples of correct TypeScript code for this rule with the { "ignoreClassesWithImplements": "public-fields" } option:
/*eslint class-methods-use-this: ["error", { "ignoreClassesWithImplements": "public-fields" }] */
interface Base {
method(): void;
}
class Derived implements Base {
method() {}
property = () => {};
}
何时不使用
🌐 When Not To Use It
修复此规则的违规行为几乎总是一个破坏性更改,因为它需要在受影响方法的每个使用处进行更改。因此,如果你的项目有不能破坏的下游使用者,或者你不希望对方法的每个调用点进行大规模更改,那么解决此规则的违规行为可能没有意义。
🌐 Fixing violations of this rule almost always is a breaking change, since it requires a change at every usage of the affected method. Therefore, if your project has downstream consumers you cannot break, or you do not wish to make invasive changes to every call site of a method, it likely does not make sense to address violations of this rule.
版本
此规则是在 ESLint v3.4.0 中引入。