Skip to content

Commit 29dbabe

Browse files
authored
In JS, fix contextual type of this assignments (microsoft#26743)
in object literal methods inside an object literal with a type annotation. Note that this does not change: 1. The type of `this` in object literal methods. 2. The fact that this-property assignments are still declarations. They just don't block contextual typing like most declarations do. This change is a bit expensive. It first calls getThisContainer, which walks the tree upward. Then it calls checkThisExpression, which will usually call getContextualType on the object literal method. If the new code then returns true, it will proceed to redo much of that work. Calling checkThisExpression should not cause incorrect circularity failures; we only have to inspect the shape of the object literal and not the types of its properties to determine its type.
1 parent 7b4f864 commit 29dbabe

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16140,6 +16140,16 @@ namespace ts {
1614016140
return true;
1614116141
}
1614216142
case SpecialPropertyAssignmentKind.ThisProperty:
16143+
if (!binaryExpression.symbol ||
16144+
binaryExpression.symbol.valueDeclaration && !!getJSDocTypeTag(binaryExpression.symbol.valueDeclaration)) {
16145+
return true;
16146+
}
16147+
const thisAccess = binaryExpression.left as PropertyAccessExpression;
16148+
if (!isObjectLiteralMethod(getThisContainer(thisAccess.expression, /*includeArrowFunctions*/ false))) {
16149+
return false;
16150+
}
16151+
const thisType = checkThisExpression(thisAccess.expression);
16152+
return thisType && !!getPropertyOfType(thisType, thisAccess.name.escapedText);
1614316153
case SpecialPropertyAssignmentKind.ModuleExports:
1614416154
return !binaryExpression.symbol || binaryExpression.symbol.valueDeclaration && !!getJSDocTypeTag(binaryExpression.symbol.valueDeclaration);
1614516155
default:
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
=== tests/cases/conformance/salsa/bug25926.js ===
2+
/** @type {{ a(): void; b?(n: number): number; }} */
3+
const o1 = {
4+
>o1 : Symbol(o1, Decl(bug25926.js, 1, 5))
5+
6+
a() {
7+
>a : Symbol(a, Decl(bug25926.js, 1, 12))
8+
9+
this.b = n => n;
10+
>this.b : Symbol(b, Decl(bug25926.js, 0, 23))
11+
>this : Symbol(__type, Decl(bug25926.js, 0, 11))
12+
>b : Symbol(b, Decl(bug25926.js, 2, 9))
13+
>n : Symbol(n, Decl(bug25926.js, 3, 16))
14+
>n : Symbol(n, Decl(bug25926.js, 3, 16))
15+
}
16+
};
17+
18+
/** @type {{ d(): void; e?(n: number): number; f?(n: number): number; g?: number }} */
19+
const o2 = {
20+
>o2 : Symbol(o2, Decl(bug25926.js, 8, 5))
21+
22+
d() {
23+
>d : Symbol(d, Decl(bug25926.js, 8, 12))
24+
25+
this.e = this.f = m => this.g || m;
26+
>this.e : Symbol(e, Decl(bug25926.js, 7, 23))
27+
>this : Symbol(__type, Decl(bug25926.js, 7, 11))
28+
>e : Symbol(e, Decl(bug25926.js, 9, 9))
29+
>this.f : Symbol(f, Decl(bug25926.js, 7, 46))
30+
>this : Symbol(__type, Decl(bug25926.js, 7, 11))
31+
>f : Symbol(f, Decl(bug25926.js, 10, 16))
32+
>m : Symbol(m, Decl(bug25926.js, 10, 25))
33+
>this.g : Symbol(g, Decl(bug25926.js, 7, 69))
34+
>this : Symbol(__type, Decl(bug25926.js, 7, 11))
35+
>g : Symbol(g, Decl(bug25926.js, 7, 69))
36+
>m : Symbol(m, Decl(bug25926.js, 10, 25))
37+
}
38+
};
39+
40+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
=== tests/cases/conformance/salsa/bug25926.js ===
2+
/** @type {{ a(): void; b?(n: number): number; }} */
3+
const o1 = {
4+
>o1 : { a(): void; }
5+
>{ a() { this.b = n => n; }} : { a(): void; }
6+
7+
a() {
8+
>a : () => void
9+
10+
this.b = n => n;
11+
>this.b = n => n : (n: number) => number
12+
>this.b : ((n: number) => number) | undefined
13+
>this : { a(): void; }
14+
>b : ((n: number) => number) | undefined
15+
>n => n : (n: number) => number
16+
>n : number
17+
>n : number
18+
}
19+
};
20+
21+
/** @type {{ d(): void; e?(n: number): number; f?(n: number): number; g?: number }} */
22+
const o2 = {
23+
>o2 : { d(): void; g?: number | undefined; }
24+
>{ d() { this.e = this.f = m => this.g || m; }} : { d(): void; }
25+
26+
d() {
27+
>d : () => void
28+
29+
this.e = this.f = m => this.g || m;
30+
>this.e = this.f = m => this.g || m : (m: number) => number
31+
>this.e : ((n: number) => number) | undefined
32+
>this : { d(): void; g?: number | undefined; }
33+
>e : ((n: number) => number) | undefined
34+
>this.f = m => this.g || m : (m: number) => number
35+
>this.f : ((n: number) => number) | undefined
36+
>this : { d(): void; g?: number | undefined; }
37+
>f : ((n: number) => number) | undefined
38+
>m => this.g || m : (m: number) => number
39+
>m : number
40+
>this.g || m : number
41+
>this.g : number | undefined
42+
>this : { d(): void; g?: number | undefined; }
43+
>g : number | undefined
44+
>m : number
45+
}
46+
};
47+
48+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @noEmit: true
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @strict: true
5+
// @Filename: bug25926.js
6+
7+
/** @type {{ a(): void; b?(n: number): number; }} */
8+
const o1 = {
9+
a() {
10+
this.b = n => n;
11+
}
12+
};
13+
14+
/** @type {{ d(): void; e?(n: number): number; f?(n: number): number; g?: number }} */
15+
const o2 = {
16+
d() {
17+
this.e = this.f = m => this.g || m;
18+
}
19+
};
20+

0 commit comments

Comments
 (0)