In JS, fix contextual type of this assignments (#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.
This commit is contained in:
parent
7b4f864b49
commit
29dbabe2e1
|
@ -16140,6 +16140,16 @@ namespace ts {
|
|||
return true;
|
||||
}
|
||||
case SpecialPropertyAssignmentKind.ThisProperty:
|
||||
if (!binaryExpression.symbol ||
|
||||
binaryExpression.symbol.valueDeclaration && !!getJSDocTypeTag(binaryExpression.symbol.valueDeclaration)) {
|
||||
return true;
|
||||
}
|
||||
const thisAccess = binaryExpression.left as PropertyAccessExpression;
|
||||
if (!isObjectLiteralMethod(getThisContainer(thisAccess.expression, /*includeArrowFunctions*/ false))) {
|
||||
return false;
|
||||
}
|
||||
const thisType = checkThisExpression(thisAccess.expression);
|
||||
return thisType && !!getPropertyOfType(thisType, thisAccess.name.escapedText);
|
||||
case SpecialPropertyAssignmentKind.ModuleExports:
|
||||
return !binaryExpression.symbol || binaryExpression.symbol.valueDeclaration && !!getJSDocTypeTag(binaryExpression.symbol.valueDeclaration);
|
||||
default:
|
||||
|
|
40
tests/baselines/reference/typeFromContextualThisType.symbols
Normal file
40
tests/baselines/reference/typeFromContextualThisType.symbols
Normal file
|
@ -0,0 +1,40 @@
|
|||
=== tests/cases/conformance/salsa/bug25926.js ===
|
||||
/** @type {{ a(): void; b?(n: number): number; }} */
|
||||
const o1 = {
|
||||
>o1 : Symbol(o1, Decl(bug25926.js, 1, 5))
|
||||
|
||||
a() {
|
||||
>a : Symbol(a, Decl(bug25926.js, 1, 12))
|
||||
|
||||
this.b = n => n;
|
||||
>this.b : Symbol(b, Decl(bug25926.js, 0, 23))
|
||||
>this : Symbol(__type, Decl(bug25926.js, 0, 11))
|
||||
>b : Symbol(b, Decl(bug25926.js, 2, 9))
|
||||
>n : Symbol(n, Decl(bug25926.js, 3, 16))
|
||||
>n : Symbol(n, Decl(bug25926.js, 3, 16))
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {{ d(): void; e?(n: number): number; f?(n: number): number; g?: number }} */
|
||||
const o2 = {
|
||||
>o2 : Symbol(o2, Decl(bug25926.js, 8, 5))
|
||||
|
||||
d() {
|
||||
>d : Symbol(d, Decl(bug25926.js, 8, 12))
|
||||
|
||||
this.e = this.f = m => this.g || m;
|
||||
>this.e : Symbol(e, Decl(bug25926.js, 7, 23))
|
||||
>this : Symbol(__type, Decl(bug25926.js, 7, 11))
|
||||
>e : Symbol(e, Decl(bug25926.js, 9, 9))
|
||||
>this.f : Symbol(f, Decl(bug25926.js, 7, 46))
|
||||
>this : Symbol(__type, Decl(bug25926.js, 7, 11))
|
||||
>f : Symbol(f, Decl(bug25926.js, 10, 16))
|
||||
>m : Symbol(m, Decl(bug25926.js, 10, 25))
|
||||
>this.g : Symbol(g, Decl(bug25926.js, 7, 69))
|
||||
>this : Symbol(__type, Decl(bug25926.js, 7, 11))
|
||||
>g : Symbol(g, Decl(bug25926.js, 7, 69))
|
||||
>m : Symbol(m, Decl(bug25926.js, 10, 25))
|
||||
}
|
||||
};
|
||||
|
||||
|
48
tests/baselines/reference/typeFromContextualThisType.types
Normal file
48
tests/baselines/reference/typeFromContextualThisType.types
Normal file
|
@ -0,0 +1,48 @@
|
|||
=== tests/cases/conformance/salsa/bug25926.js ===
|
||||
/** @type {{ a(): void; b?(n: number): number; }} */
|
||||
const o1 = {
|
||||
>o1 : { a(): void; }
|
||||
>{ a() { this.b = n => n; }} : { a(): void; }
|
||||
|
||||
a() {
|
||||
>a : () => void
|
||||
|
||||
this.b = n => n;
|
||||
>this.b = n => n : (n: number) => number
|
||||
>this.b : ((n: number) => number) | undefined
|
||||
>this : { a(): void; }
|
||||
>b : ((n: number) => number) | undefined
|
||||
>n => n : (n: number) => number
|
||||
>n : number
|
||||
>n : number
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {{ d(): void; e?(n: number): number; f?(n: number): number; g?: number }} */
|
||||
const o2 = {
|
||||
>o2 : { d(): void; g?: number | undefined; }
|
||||
>{ d() { this.e = this.f = m => this.g || m; }} : { d(): void; }
|
||||
|
||||
d() {
|
||||
>d : () => void
|
||||
|
||||
this.e = this.f = m => this.g || m;
|
||||
>this.e = this.f = m => this.g || m : (m: number) => number
|
||||
>this.e : ((n: number) => number) | undefined
|
||||
>this : { d(): void; g?: number | undefined; }
|
||||
>e : ((n: number) => number) | undefined
|
||||
>this.f = m => this.g || m : (m: number) => number
|
||||
>this.f : ((n: number) => number) | undefined
|
||||
>this : { d(): void; g?: number | undefined; }
|
||||
>f : ((n: number) => number) | undefined
|
||||
>m => this.g || m : (m: number) => number
|
||||
>m : number
|
||||
>this.g || m : number
|
||||
>this.g : number | undefined
|
||||
>this : { d(): void; g?: number | undefined; }
|
||||
>g : number | undefined
|
||||
>m : number
|
||||
}
|
||||
};
|
||||
|
||||
|
20
tests/cases/conformance/salsa/typeFromContextualThisType.ts
Normal file
20
tests/cases/conformance/salsa/typeFromContextualThisType.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
// @noEmit: true
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @strict: true
|
||||
// @Filename: bug25926.js
|
||||
|
||||
/** @type {{ a(): void; b?(n: number): number; }} */
|
||||
const o1 = {
|
||||
a() {
|
||||
this.b = n => n;
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {{ d(): void; e?(n: number): number; f?(n: number): number; g?: number }} */
|
||||
const o2 = {
|
||||
d() {
|
||||
this.e = this.f = m => this.g || m;
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in a new issue