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:
Nathan Shively-Sanders 2018-08-29 15:06:38 -07:00 committed by GitHub
parent 7b4f864b49
commit 29dbabe2e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 0 deletions

View file

@ -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:

View 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))
}
};

View 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
}
};

View 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;
}
};