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, например:
class Person {
sayHi() {
console.log("Hi!");
}
}
const person = new Person();
person.sayHi(); // => "Hi!"
Если метод экземпляра класса не использует this, это обычно означает, что он не обращается к состоянию экземпляра и поэтому ему не обязательно быть методом. Следовательно, его иногда можно преобразовать в обычную функцию или статический метод, что может лучше передать замысел пользователям API.
В приведённом выше примере метод sayHi не использует this, поэтому его можно сделать обычной функцией:
// 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();
Также возможно, что автор забыл использовать часть данных экземпляра, которую намеревался включить.
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!'
Подробности правила
Это правило отмечает методы экземпляров класса, которые не используют this.
Примеры неправильного кода для этого правила:
/*eslint class-methods-use-this: "error"*/
class A {
() {
console.log("Hello World"); /* error Expected 'this' to be used by class method 'foo'. */
}
}
Примеры правильного кода для этого правила:
/*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.
}
}
Параметры
У этого правила есть четыре параметра:
-
"exceptMethods"позволяет указать имена методов, которые следует игнорировать при проверке. -
"enforceForClassFields"требует, чтобы стрелочные функции и функциональные выражения, используемые в качестве инициализаторов полей экземпляра, использовалиthis. Это также относится к полям с автоаксессорами (полям, объявленным с помощью ключевого словаaccessor), которые входят в предложение декораторов этапа 3. (значение по умолчанию:true) -
"ignoreOverrideMethods"игнорирует члены, помеченные модификаторомoverride. (только TypeScript, значение по умолчанию:false) -
"ignoreClassesWithImplements"игнорирует члены класса, определённые в классе, которыйimplementsинтерфейс. (только TypeScript)
exceptMethods
"class-methods-use-this": [<enabled>, { "exceptMethods": [<...exceptions>] }]
Параметр "exceptMethods" позволяет передать массив имён методов, для которых следует игнорировать предупреждения. Например, у вас может быть спецификация внешней библиотеки, требующая переопределить метод обычной функцией (а не статическим методом), которая не использует this в теле функции. В этом случае вы можете добавить этот метод в список игнорируемых предупреждений.
Примеры неправильного кода для этого правила без параметра "exceptMethods":
/*eslint class-methods-use-this: "error"*/
class A {
() {
}
}
Примеры правильного кода для этого правила с параметром 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)
Примеры неправильного кода для этого правила с параметром { "enforceForClassFields": true } (по умолчанию):
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
class A {
() => {}
}
Примеры правильного кода для этого правила с параметром { "enforceForClassFields": true } (по умолчанию):
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
class A {
foo = () => {this;}
}
Примеры правильного кода для этого правила с параметром { "enforceForClassFields": false }:
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": false }] */
class A {
foo = () => {}
}
Примеры неправильного кода TypeScript для этого правила с параметром { "enforceForClassFields": true } (по умолчанию):
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
class A {
() => {}
accessor bar = () {}
}
Примеры правильного кода TypeScript для этого правила с параметром { "enforceForClassFields": true } (по умолчанию):
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
class A {
foo = () => {this;}
accessor bar = () => {this;}
}
Примеры правильного кода TypeScript для этого правила с параметром { "enforceForClassFields": false }:
/*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)
Примеры неправильного кода TypeScript для этого правила с параметром { "ignoreOverrideMethods": false } (по умолчанию):
/*eslint class-methods-use-this: ["error", { "ignoreOverrideMethods": false }] */
abstract class Base {
abstract method(): void;
abstract property: () => void;
}
class Derived extends Base {
() {}
() => {};
}
Примеры правильного кода TypeScript для этого правила с параметром { "ignoreOverrideMethods": false } (по умолчанию):
/*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;
};
}
Примеры правильного кода TypeScript для этого правила с параметром { "ignoreOverrideMethods": true }:
/*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 интерфейс. Параметр принимает два возможных значения:
-
"all"— игнорировать все классы, реализующие интерфейсы -
"public-fields"— игнорировать только открытые поля в классах, реализующих интерфейсы
Примеры неправильного кода TypeScript для этого правила с параметром { "ignoreClassesWithImplements": "all" }:
/*eslint class-methods-use-this: ["error", { "ignoreClassesWithImplements": "all" }] */
class Standalone {
() {}
() => {};
}
Примеры правильного кода TypeScript для этого правила с параметром { "ignoreClassesWithImplements": "all" }:
/*eslint class-methods-use-this: ["error", { "ignoreClassesWithImplements": "all" }] */
interface Base {
method(): void;
}
class Derived implements Base {
method() {}
property = () => {};
}
Примеры неправильного кода TypeScript для этого правила с параметром { "ignoreClassesWithImplements": "public-fields" }:
/*eslint class-methods-use-this: ["error", { "ignoreClassesWithImplements": "public-fields" }] */
interface Base {
method(): void;
}
class Derived implements Base {
method() {}
property = () => {};
() {}
() => {};
() {}
() => {};
}
Примеры правильного кода TypeScript для этого правила с параметром { "ignoreClassesWithImplements": "public-fields" }:
/*eslint class-methods-use-this: ["error", { "ignoreClassesWithImplements": "public-fields" }] */
interface Base {
method(): void;
}
class Derived implements Base {
method() {}
property = () => {};
}
Когда не следует использовать это правило
Исправление нарушений этого правила почти всегда является несовместимым изменением, поскольку требует изменить каждое место использования затронутого метода. Поэтому, если у вашего проекта есть зависимые потребители, для которых нельзя допустить несовместимых изменений, или вы не хотите вносить масштабные изменения во все места вызова метода, устранять нарушения этого правила, скорее всего, не имеет смысла.
Версия
Это правило добавлено в ESLint v3.4.0.
Дополнительные материалы
Ресурсы
© OpenJS Foundation and other contributors
Licensed under the MIT License.
https://eslint.org/docs/latest/rules/class-methods-use-this