You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) are often used to encapsulate reusable logic, especially stateful logic, into an object where each instance's state is accessed via `this`. When an API is written with an instance method, it signals to consumers:
9
10
10
-
If a class method does not use `this`, it can *sometimes* be made into a static function. If you do convert the method into a static function, instances of the class that call that particular method have to be converted to a static call as well (`MyClass.callStaticMethod()`).
11
+
- The method's outcome is related to the object on which it's invoked, including possibly its state.
11
12
12
-
It's possible to have a class method which doesn't use `this`, such as:
13
+
```js
14
+
const array1 = [1, 2, 3];
15
+
const array2 = [4, 5, 6];
13
16
14
-
```js
15
-
classA {
16
-
constructor() {
17
-
this.a="hi";
18
-
}
17
+
// Using the `includes()` method on different objects gives different results:
18
+
array1.includes(1); // true
19
+
array2.includes(1); // false
19
20
20
-
print() {
21
-
console.log(this.a);
22
-
}
21
+
// Modifying the state of an object may change the outcome of its instance methods:
22
+
array2.push(1);
23
+
array2.includes(1); // true
24
+
```
25
+
26
+
- The method doesn't make sense to be used without an associated object. (For example, it doesn't make sense to call `Array#includes()` without an array to operate on.)
23
27
28
+
It's possible to have a class method which doesn't use `this`, such as:
29
+
30
+
```js
31
+
classPerson {
24
32
sayHi() {
25
-
console.log("hi");
33
+
console.log("Hi!");
26
34
}
27
35
}
28
36
29
-
let a=newA();
30
-
a.sayHi(); // => "hi"
37
+
constperson=newPerson();
38
+
person.sayHi(); // => "Hi!"
31
39
```
32
40
33
-
In the example above, the `sayHi` method doesn't use `this`, so we can make it a static method:
41
+
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.
42
+
Therefore, it can *sometimes* be refactored into an [ordinary function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions) or a [static method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static), which may better communicate intent to users of the API.
43
+
44
+
In the example above, the `sayHi` method doesn't use `this`, so we can make it an ordinary function:
34
45
35
46
```js
36
-
classA {
37
-
constructor() {
38
-
this.a="hi";
39
-
}
47
+
// Ordinary function
48
+
functionsayHi() {
49
+
console.log("Hi!");
50
+
}
40
51
41
-
print() {
42
-
console.log(this.a);
43
-
}
52
+
// No need for `Person` class or any instance thereof
53
+
sayHi(); // => "Hi!"
44
54
55
+
// Alternately, a static method may be used if it offers a more natural API
56
+
classPerson {
45
57
staticsayHi() {
46
-
console.log("hi");
58
+
console.log("Hi!");
47
59
}
48
60
}
49
61
50
-
A.sayHi(); // => "hi"
62
+
Person.sayHi(); // => "Hi!"
63
+
64
+
// Keep in mind that, either way, the following now throws an error,
65
+
// since sayHi() is no longer an instance method!
66
+
//
67
+
// const person = new Person();
68
+
// person.sayHi();
51
69
```
52
70
53
-
Also note in the above examples that if you switch a method to a static method, *instances* of the class that call the static method (`let a = new A(); a.sayHi();`) have to be updated to being a static call (`A.sayHi();`) instead of having the instance of the *class* call the method.
71
+
It's also possible the author forgot to use some piece of instance data that they intended to include.
72
+
73
+
```js
74
+
classPerson {
75
+
constructor(name) {
76
+
this.name= name;
77
+
}
78
+
79
+
sayHi() {
80
+
console.log(`Hi from ${this.name}!`);
81
+
}
82
+
}
83
+
84
+
constalice=newPerson('Alice');
85
+
alice.sayHi(); // => 'Hi from Alice!'
86
+
87
+
constbob=newPerson('Bob');
88
+
bob.sayHi(); // => 'Hi from Bob!'
89
+
```
54
90
55
91
## Rule Details
56
92
57
-
This rule is aimed to flag class methods that do not use `this`.
93
+
This rule flags class instance methods that do not use `this`.
58
94
59
95
Examples of **incorrect** code for this rule:
60
96
@@ -65,7 +101,7 @@ Examples of **incorrect** code for this rule:
65
101
66
102
classA {
67
103
foo() {
68
-
console.log("Hello World"); /*error Expected 'this' to be used by class method 'foo'.*/
104
+
console.log("Hello World"); /*error Expected 'this' to be used by class method 'foo'.*/
69
105
}
70
106
}
71
107
```
@@ -81,7 +117,7 @@ Examples of **correct** code for this rule:
81
117
82
118
classA {
83
119
foo() {
84
-
this.bar="Hello World"; // OK, this is used
120
+
this.bar="Hello World"; // OK, `this` is used
85
121
}
86
122
}
87
123
@@ -108,10 +144,10 @@ class C {
108
144
109
145
This rule has four options:
110
146
111
-
*`"exceptMethods"` allows specified method names to be ignored with this rule.
112
-
*`"enforceForClassFields"` 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](https://github.com/tc39/proposal-decorators). (default: `true`)
113
-
*`"ignoreOverrideMethods"` ignores members that are marked with the `override` modifier. (TypeScript only, default: `false`)
114
-
*`"ignoreClassesWithImplements"` ignores class members that are defined within a class that `implements` an interface. (TypeScript only)
147
+
-`"exceptMethods"` allows specified method names to be ignored with this rule.
148
+
-`"enforceForClassFields"` 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](https://github.com/tc39/proposal-decorators). (default: `true`)
149
+
-`"ignoreOverrideMethods"` ignores members that are marked with the [`override` modifier](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-3.html#override-and-the---noimplicitoverride-flag). (TypeScript only, default: `false`)
150
+
-`"ignoreClassesWithImplements"` ignores class members that are defined within a class that [`implements`](https://www.typescriptlang.org/docs/handbook/2/classes.html#implements-clauses) an interface. (TypeScript only)
115
151
116
152
### exceptMethods
117
153
@@ -328,8 +364,8 @@ class Derived extends Base {
328
364
329
365
The `ignoreClassesWithImplements` ignores class members that are defined within a class that `implements` an interface. The option accepts two possible values:
330
366
331
-
*`"all"` - Ignores all classes that implement interfaces
332
-
*`"public-fields"` - Only ignores public fields in classes that implement interfaces
367
+
-`"all"` - Ignores all classes that implement interfaces
368
+
-`"public-fields"` - Only ignores public fields in classes that implement interfaces
333
369
334
370
Examples of **incorrect** TypeScript code for this rule with the `{ "ignoreClassesWithImplements": "all" }`:
335
371
@@ -408,3 +444,9 @@ class Derived implements Base {
408
444
```
409
445
410
446
:::
447
+
448
+
449
+
## When Not To Use It
450
+
451
+
Fixing violations of this rule almost always is a breaking change, since it requires a change at every usage of the affected method.
452
+
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.
0 commit comments