Skip to content

Commit dccb6ee

Browse files
authored
fix(eslint-plugin): [no-invalid-this] allow "this" in class property definitions (typescript-eslint#2685)
1 parent 30a6951 commit dccb6ee

File tree

2 files changed

+83
-7
lines changed

2 files changed

+83
-7
lines changed

packages/eslint-plugin/src/rules/no-invalid-this.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,33 @@ export default createRule<Options, MessageIds>({
3131
defaultOptions: [{ capIsConstructor: true }],
3232
create(context) {
3333
const rules = baseRule.create(context);
34-
const argList: boolean[] = [];
34+
35+
/**
36+
* Since function definitions can be nested we use a stack storing if "this" is valid in the current context.
37+
*
38+
* Example:
39+
*
40+
* function a(this: number) { // valid "this"
41+
* function b() {
42+
* console.log(this); // invalid "this"
43+
* }
44+
* }
45+
*
46+
* When parsing the function declaration of "a" the stack will be: [true]
47+
* When parsing the function declaration of "b" the stack will be: [true, false]
48+
*/
49+
const thisIsValidStack: boolean[] = [];
3550

3651
return {
3752
...rules,
53+
ClassProperty(): void {
54+
thisIsValidStack.push(true);
55+
},
56+
'ClassProperty:exit'(): void {
57+
thisIsValidStack.pop();
58+
},
3859
FunctionDeclaration(node: TSESTree.FunctionDeclaration): void {
39-
argList.push(
60+
thisIsValidStack.push(
4061
node.params.some(
4162
param =>
4263
param.type === AST_NODE_TYPES.Identifier && param.name === 'this',
@@ -46,12 +67,12 @@ export default createRule<Options, MessageIds>({
4667
rules.FunctionDeclaration(node);
4768
},
4869
'FunctionDeclaration:exit'(node: TSESTree.FunctionDeclaration): void {
49-
argList.pop();
70+
thisIsValidStack.pop();
5071
// baseRule's work
5172
rules['FunctionDeclaration:exit'](node);
5273
},
5374
FunctionExpression(node: TSESTree.FunctionExpression): void {
54-
argList.push(
75+
thisIsValidStack.push(
5576
node.params.some(
5677
param =>
5778
param.type === AST_NODE_TYPES.Identifier && param.name === 'this',
@@ -61,14 +82,14 @@ export default createRule<Options, MessageIds>({
6182
rules.FunctionExpression(node);
6283
},
6384
'FunctionExpression:exit'(node: TSESTree.FunctionExpression): void {
64-
argList.pop();
85+
thisIsValidStack.pop();
6586
// baseRule's work
6687
rules['FunctionExpression:exit'](node);
6788
},
6889
ThisExpression(node: TSESTree.ThisExpression): void {
69-
const lastFnArg = argList[argList.length - 1];
90+
const thisIsValidHere = thisIsValidStack[thisIsValidStack.length - 1];
7091

71-
if (lastFnArg) {
92+
if (thisIsValidHere) {
7293
return;
7394
}
7495

packages/eslint-plugin/tests/rules/no-invalid-this.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,28 @@ class A {
277277
}
278278
`,
279279

280+
// Class Properties.
281+
`
282+
class A {
283+
b = 0;
284+
c = this.b;
285+
}
286+
`,
287+
288+
`
289+
class A {
290+
b = new Array(this, 1, 2, 3);
291+
}
292+
`,
293+
294+
`
295+
class A {
296+
b = () => {
297+
console.log(this);
298+
};
299+
}
300+
`,
301+
280302
// Array methods.
281303

282304
`
@@ -613,6 +635,9 @@ obj.foo = function () {
613635

614636
errors,
615637
},
638+
639+
// Class Methods.
640+
616641
{
617642
code: `
618643
class A {
@@ -628,6 +653,36 @@ class A {
628653
errors,
629654
},
630655

656+
// Class Properties.
657+
658+
{
659+
code: `
660+
class A {
661+
b = new Array(1, 2, function () {
662+
console.log(this);
663+
z(x => console.log(x, this));
664+
});
665+
}
666+
`,
667+
668+
errors,
669+
},
670+
671+
{
672+
code: `
673+
class A {
674+
b = () => {
675+
function c() {
676+
console.log(this);
677+
z(x => console.log(x, this));
678+
}
679+
};
680+
}
681+
`,
682+
683+
errors,
684+
},
685+
631686
// Class Static methods.
632687

633688
{

0 commit comments

Comments
 (0)