Skip to content

Commit 0b2b9e0

Browse files
atlarix-agentAmariahAK
authored andcommitted
Fix inconsistent typeof this with contextual this parameter (#63616)
The binder tracked seenThisKeyword only for ThisKeyword AST nodes, but typeof this in type queries uses an Identifier node with text "this". This caused NodeFlags.ContainsThis to not be set on functions that only reference this via typeof this, which in turn prevented hasContextSensitiveParameters from recognizing the function as context-sensitive, causing getContextualThisParameterType to skip the contextual this type from the calling signature. Fix: extend the seenThisKeyword check in the binder to also recognize Identifier nodes with escapedText === "this", which is how the parser represents this inside typeof type queries. Co-authored-by: atlarix-agent <agent@atlarix.dev>
1 parent 637d574 commit 0b2b9e0

5 files changed

Lines changed: 482 additions & 4 deletions

File tree

src/compiler/binder.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2859,10 +2859,10 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
28592859
break;
28602860
}
28612861
// falls through
2862-
case SyntaxKind.ThisKeyword:
2863-
if (node.kind === SyntaxKind.ThisKeyword) {
2864-
seenThisKeyword = true;
2865-
}
2862+
case SyntaxKind.ThisKeyword:
2863+
if (node.kind === SyntaxKind.ThisKeyword || (node.kind === SyntaxKind.Identifier && (node as Identifier).escapedText === "this")) {
2864+
seenThisKeyword = true;
2865+
}
28662866
// TODO: Why use `isExpression` here? both Identifier and ThisKeyword are expressions.
28672867
if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) {
28682868
(node as Identifier | ThisExpression).flowNode = currentFlow;
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//// [tests/cases/compiler/typeofThisWithContextualThisParameter.ts] ////
2+
3+
//// [typeofThisWithContextualThisParameter.ts]
4+
// Reproduces #63616: typeof this inconsistent when this parameter comes from a contextual signature
5+
6+
interface A { a: string }
7+
8+
// ---- Object-literal method case ----
9+
10+
{
11+
interface B { g: (this: A) => void }
12+
13+
function f(_: B): void {}
14+
15+
// Case 1: typeof this without explicit this usage — should resolve to A
16+
f({
17+
g() {
18+
type X = typeof this;
19+
// ^ should be A
20+
}
21+
})
22+
23+
// Case 2: this used before typeof this — should resolve to A
24+
f({
25+
g() {
26+
this;
27+
type X = typeof this;
28+
// ^ should be A
29+
}
30+
})
31+
32+
// Case 3: this used after typeof this — should resolve to A
33+
f({
34+
g() {
35+
type X = typeof this;
36+
// ^ should be A
37+
this;
38+
}
39+
})
40+
}
41+
42+
// ---- Function expression case ----
43+
44+
{
45+
function f(_: (this: A) => void): void {}
46+
47+
// Case 4: typeof this only — should resolve to A (not implicit any error for typeof this context)
48+
f(function g() {
49+
type X = typeof this;
50+
})
51+
52+
// Case 5: this used before typeof this — should resolve to A
53+
f(function g() {
54+
this;
55+
type X = typeof this;
56+
})
57+
58+
// Case 6: this used after typeof this — should resolve to A
59+
f(function g() {
60+
type X = typeof this;
61+
this;
62+
})
63+
}
64+
65+
66+
//// [typeofThisWithContextualThisParameter.js]
67+
"use strict";
68+
// Reproduces #63616: typeof this inconsistent when this parameter comes from a contextual signature
69+
// ---- Object-literal method case ----
70+
{
71+
function f(_) { }
72+
// Case 1: typeof this without explicit this usage — should resolve to A
73+
f({
74+
g() {
75+
// ^ should be A
76+
}
77+
});
78+
// Case 2: this used before typeof this — should resolve to A
79+
f({
80+
g() {
81+
this;
82+
// ^ should be A
83+
}
84+
});
85+
// Case 3: this used after typeof this — should resolve to A
86+
f({
87+
g() {
88+
// ^ should be A
89+
this;
90+
}
91+
});
92+
}
93+
// ---- Function expression case ----
94+
{
95+
function f(_) { }
96+
// Case 4: typeof this only — should resolve to A (not implicit any error for typeof this context)
97+
f(function g() {
98+
});
99+
// Case 5: this used before typeof this — should resolve to A
100+
f(function g() {
101+
this;
102+
});
103+
// Case 6: this used after typeof this — should resolve to A
104+
f(function g() {
105+
this;
106+
});
107+
}
108+
109+
110+
//// [typeofThisWithContextualThisParameter.d.ts]
111+
interface A {
112+
a: string;
113+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//// [tests/cases/compiler/typeofThisWithContextualThisParameter.ts] ////
2+
3+
=== typeofThisWithContextualThisParameter.ts ===
4+
// Reproduces #63616: typeof this inconsistent when this parameter comes from a contextual signature
5+
6+
interface A { a: string }
7+
>A : Symbol(A, Decl(typeofThisWithContextualThisParameter.ts, 0, 0))
8+
>a : Symbol(A.a, Decl(typeofThisWithContextualThisParameter.ts, 2, 13))
9+
10+
// ---- Object-literal method case ----
11+
12+
{
13+
interface B { g: (this: A) => void }
14+
>B : Symbol(B, Decl(typeofThisWithContextualThisParameter.ts, 6, 1))
15+
>g : Symbol(B.g, Decl(typeofThisWithContextualThisParameter.ts, 7, 17))
16+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22))
17+
>A : Symbol(A, Decl(typeofThisWithContextualThisParameter.ts, 0, 0))
18+
19+
function f(_: B): void {}
20+
>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 7, 40))
21+
>_ : Symbol(_, Decl(typeofThisWithContextualThisParameter.ts, 9, 15))
22+
>B : Symbol(B, Decl(typeofThisWithContextualThisParameter.ts, 6, 1))
23+
24+
// Case 1: typeof this without explicit this usage — should resolve to A
25+
f({
26+
>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 7, 40))
27+
28+
g() {
29+
>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 12, 7))
30+
31+
type X = typeof this;
32+
>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 13, 13))
33+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22))
34+
35+
// ^ should be A
36+
}
37+
})
38+
39+
// Case 2: this used before typeof this — should resolve to A
40+
f({
41+
>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 7, 40))
42+
43+
g() {
44+
>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 20, 7))
45+
46+
this;
47+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22))
48+
49+
type X = typeof this;
50+
>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 22, 17))
51+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22))
52+
53+
// ^ should be A
54+
}
55+
})
56+
57+
// Case 3: this used after typeof this — should resolve to A
58+
f({
59+
>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 7, 40))
60+
61+
g() {
62+
>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 29, 7))
63+
64+
type X = typeof this;
65+
>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 30, 13))
66+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22))
67+
68+
// ^ should be A
69+
this;
70+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22))
71+
}
72+
})
73+
}
74+
75+
// ---- Function expression case ----
76+
77+
{
78+
function f(_: (this: A) => void): void {}
79+
>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 40, 1))
80+
>_ : Symbol(_, Decl(typeofThisWithContextualThisParameter.ts, 41, 15))
81+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19))
82+
>A : Symbol(A, Decl(typeofThisWithContextualThisParameter.ts, 0, 0))
83+
84+
// Case 4: typeof this only — should resolve to A (not implicit any error for typeof this context)
85+
f(function g() {
86+
>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 40, 1))
87+
>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 44, 6))
88+
89+
type X = typeof this;
90+
>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 44, 20))
91+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19))
92+
93+
})
94+
95+
// Case 5: this used before typeof this — should resolve to A
96+
f(function g() {
97+
>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 40, 1))
98+
>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 49, 6))
99+
100+
this;
101+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19))
102+
103+
type X = typeof this;
104+
>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 50, 13))
105+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19))
106+
107+
})
108+
109+
// Case 6: this used after typeof this — should resolve to A
110+
f(function g() {
111+
>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 40, 1))
112+
>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 55, 6))
113+
114+
type X = typeof this;
115+
>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 55, 20))
116+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19))
117+
118+
this;
119+
>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19))
120+
121+
})
122+
}
123+

0 commit comments

Comments
 (0)