Add missing contextual type to static PropertyDeclarations

Makes it possible to type class static fields.

Fixes #33897.
This commit is contained in:
Eli Barzilay 2020-08-17 20:24:41 -04:00
parent 598e9b2e88
commit f9e360d44b
8 changed files with 390 additions and 38 deletions

View File

@ -22983,7 +22983,11 @@ namespace ts {
return getContextuallyTypedParameterType(declaration);
case SyntaxKind.BindingElement:
return getContextualTypeForBindingElement(declaration);
// By default, do nothing and return undefined - only parameters and binding elements have context implied by a parent
case SyntaxKind.PropertyDeclaration:
if (hasSyntacticModifier(declaration, ModifierFlags.Static)) {
return getContextualTypeForStaticPropertyDeclaration(declaration);
}
// By default, do nothing and return undefined - only the above cases have context implied by a parent
}
}
@ -23001,6 +23005,12 @@ namespace ts {
}
}
function getContextualTypeForStaticPropertyDeclaration(declaration: PropertyDeclaration): Type | undefined {
const parentType = isExpression(declaration.parent) && getContextualType(declaration.parent);
if (!parentType) return undefined;
return getTypeOfPropertyOfContextualType(parentType, getSymbolOfNode(declaration).escapedName);
}
// In a variable, parameter or property declaration with a type annotation,
// the contextual type of an initializer expression is the type of the variable, parameter or property.
// Otherwise, in a parameter declaration of a contextually typed function expression,

View File

@ -1,12 +1,8 @@
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(16,24): error TS7006: Parameter 'arg' implicitly has an 'any' type.
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(19,24): error TS7006: Parameter 'arg' implicitly has an 'any' type.
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(27,27): error TS7006: Parameter 'arg' implicitly has an 'any' type.
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(30,27): error TS7006: Parameter 'arg' implicitly has an 'any' type.
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(38,36): error TS7006: Parameter 'arg' implicitly has an 'any' type.
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(41,36): error TS7006: Parameter 'arg' implicitly has an 'any' type.
==== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts (6 errors) ====
==== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts (2 errors) ====
interface A {
numProp: number;
}
@ -38,13 +34,9 @@ tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTyp
function getFoo2(): Foo {
return class {
static method1 = (arg) => {
~~~
!!! error TS7006: Parameter 'arg' implicitly has an 'any' type.
arg.numProp = 10;
}
static method2 = (arg) => {
~~~
!!! error TS7006: Parameter 'arg' implicitly has an 'any' type.
arg.strProp = "hello";
}
}
@ -53,13 +45,9 @@ tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTyp
function getFoo3(): Foo {
return class {
static method1 = function (arg) {
~~~
!!! error TS7006: Parameter 'arg' implicitly has an 'any' type.
arg.numProp = 10;
}
static method2 = function (arg) {
~~~
!!! error TS7006: Parameter 'arg' implicitly has an 'any' type.
arg.strProp = "hello";
}
}

View File

@ -59,14 +59,18 @@ function getFoo2(): Foo {
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 26, 26))
arg.numProp = 10;
>arg.numProp : Symbol(A.numProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 0, 13))
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 26, 26))
>numProp : Symbol(A.numProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 0, 13))
}
static method2 = (arg) => {
>method2 : Symbol((Anonymous class).method2, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 28, 9))
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 29, 26))
arg.strProp = "hello";
>arg.strProp : Symbol(B.strProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 4, 14))
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 29, 26))
>strProp : Symbol(B.strProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 4, 14))
}
}
}
@ -81,14 +85,18 @@ function getFoo3(): Foo {
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 37, 35))
arg.numProp = 10;
>arg.numProp : Symbol(A.numProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 0, 13))
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 37, 35))
>numProp : Symbol(A.numProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 0, 13))
}
static method2 = function (arg) {
>method2 : Symbol((Anonymous class).method2, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 39, 9))
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 40, 35))
arg.strProp = "hello";
>arg.strProp : Symbol(B.strProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 4, 14))
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 40, 35))
>strProp : Symbol(B.strProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 4, 14))
}
}
}

View File

@ -57,27 +57,27 @@ function getFoo2(): Foo {
>class { static method1 = (arg) => { arg.numProp = 10; } static method2 = (arg) => { arg.strProp = "hello"; } } : typeof (Anonymous class)
static method1 = (arg) => {
>method1 : (arg: any) => void
>(arg) => { arg.numProp = 10; } : (arg: any) => void
>arg : any
>method1 : (arg: A) => void
>(arg) => { arg.numProp = 10; } : (arg: A) => void
>arg : A
arg.numProp = 10;
>arg.numProp = 10 : 10
>arg.numProp : any
>arg : any
>numProp : any
>arg.numProp : number
>arg : A
>numProp : number
>10 : 10
}
static method2 = (arg) => {
>method2 : (arg: any) => void
>(arg) => { arg.strProp = "hello"; } : (arg: any) => void
>arg : any
>method2 : (arg: B) => void
>(arg) => { arg.strProp = "hello"; } : (arg: B) => void
>arg : B
arg.strProp = "hello";
>arg.strProp = "hello" : "hello"
>arg.strProp : any
>arg : any
>strProp : any
>arg.strProp : string
>arg : B
>strProp : string
>"hello" : "hello"
}
}
@ -90,27 +90,27 @@ function getFoo3(): Foo {
>class { static method1 = function (arg) { arg.numProp = 10; } static method2 = function (arg) { arg.strProp = "hello"; } } : typeof (Anonymous class)
static method1 = function (arg) {
>method1 : (arg: any) => void
>function (arg) { arg.numProp = 10; } : (arg: any) => void
>arg : any
>method1 : (arg: A) => void
>function (arg) { arg.numProp = 10; } : (arg: A) => void
>arg : A
arg.numProp = 10;
>arg.numProp = 10 : 10
>arg.numProp : any
>arg : any
>numProp : any
>arg.numProp : number
>arg : A
>numProp : number
>10 : 10
}
static method2 = function (arg) {
>method2 : (arg: any) => void
>function (arg) { arg.strProp = "hello"; } : (arg: any) => void
>arg : any
>method2 : (arg: B) => void
>function (arg) { arg.strProp = "hello"; } : (arg: B) => void
>arg : B
arg.strProp = "hello";
>arg.strProp = "hello" : "hello"
>arg.strProp : any
>arg : any
>strProp : any
>arg.strProp : string
>arg : B
>strProp : string
>"hello" : "hello"
}
}

View File

@ -0,0 +1,106 @@
//// [staticFieldWithInterfaceContext.ts]
interface I {
x: { a: "a" };
}
let c: I = class {
// should typecheck the same as the last line
static x = { a: "a" };
};
c.x = { a: "a" };
const ex = "x";
let c2: I = class { static [ex] = { a: "a" }; };
c[ex] = { a: "a" };
function f(c: I = class { static x = { a: "a" } }) { }
let { c: c3 }: { c: I } = { c: class { static x = { a: "a" } } };
let { c: c4 = class { static x = { a: "a" } }}: { c?: I } = { };
let { c: c5 = class { static x = { a: "a" } }}: { c?: I } = { c: class { static x = { a: "a" } } };
let [ c6 ]: [I] = [class { static x = { a: "a" } }];
let [ c7 ]: I[] = [class { static x = { a: "a" } }];
// These are broken because of #40158
// let [ c8 = class { static x = { a: "a" } } ]: [I?] = [];
// let [ c9 = class { static x = { a: "a" } } ]: I[] = [];
// let [ c10 = class { static x = { a: "a" } } ]: [I?] = [class { static x = { a: "a" } }];
// let [ c11 = class { static x = { a: "a" } } ]: I[] = [class { static x = { a: "a" } }];
//// [staticFieldWithInterfaceContext.js]
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
var c = (_a = /** @class */ (function () {
function class_1() {
}
return class_1;
}()),
// should typecheck the same as the last line
_a.x = { a: "a" },
_a);
c.x = { a: "a" };
var ex = "x";
var c2 = (_c = /** @class */ (function () {
function class_2() {
}
return class_2;
}()),
_b = ex,
_c[_b] = { a: "a" },
_c);
c[ex] = { a: "a" };
function f(c) {
var _a;
if (c === void 0) { c = (_a = /** @class */ (function () {
function class_3() {
}
return class_3;
}()),
_a.x = { a: "a" },
_a); }
}
var c3 = { c: (_d = /** @class */ (function () {
function class_4() {
}
return class_4;
}()),
_d.x = { a: "a" },
_d) }.c;
var _k = {}.c, c4 = _k === void 0 ? (_e = /** @class */ (function () {
function class_5() {
}
return class_5;
}()),
_e.x = { a: "a" },
_e) : _k;
var _l = { c: (_g = /** @class */ (function () {
function class_6() {
}
return class_6;
}()),
_g.x = { a: "a" },
_g) }.c, c5 = _l === void 0 ? (_f = /** @class */ (function () {
function class_7() {
}
return class_7;
}()),
_f.x = { a: "a" },
_f) : _l;
var c6 = [(_h = /** @class */ (function () {
function class_8() {
}
return class_8;
}()),
_h.x = { a: "a" },
_h)][0];
var c7 = [(_j = /** @class */ (function () {
function class_9() {
}
return class_9;
}()),
_j.x = { a: "a" },
_j)][0];
// These are broken because of #40158
// let [ c8 = class { static x = { a: "a" } } ]: [I?] = [];
// let [ c9 = class { static x = { a: "a" } } ]: I[] = [];
// let [ c10 = class { static x = { a: "a" } } ]: [I?] = [class { static x = { a: "a" } }];
// let [ c11 = class { static x = { a: "a" } } ]: I[] = [class { static x = { a: "a" } }];

View File

@ -0,0 +1,92 @@
=== tests/cases/compiler/staticFieldWithInterfaceContext.ts ===
interface I {
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
x: { a: "a" };
>x : Symbol(I.x, Decl(staticFieldWithInterfaceContext.ts, 0, 13))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 1, 8))
}
let c: I = class {
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 3, 3))
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
// should typecheck the same as the last line
static x = { a: "a" };
>x : Symbol(c.x, Decl(staticFieldWithInterfaceContext.ts, 3, 18))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 5, 16))
};
c.x = { a: "a" };
>c.x : Symbol(I.x, Decl(staticFieldWithInterfaceContext.ts, 0, 13))
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 3, 3))
>x : Symbol(I.x, Decl(staticFieldWithInterfaceContext.ts, 0, 13))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 7, 7))
const ex = "x";
>ex : Symbol(ex, Decl(staticFieldWithInterfaceContext.ts, 9, 5))
let c2: I = class { static [ex] = { a: "a" }; };
>c2 : Symbol(c2, Decl(staticFieldWithInterfaceContext.ts, 10, 3))
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
>[ex] : Symbol(c2[ex], Decl(staticFieldWithInterfaceContext.ts, 10, 19))
>ex : Symbol(ex, Decl(staticFieldWithInterfaceContext.ts, 9, 5))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 10, 35))
c[ex] = { a: "a" };
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 3, 3))
>ex : Symbol(ex, Decl(staticFieldWithInterfaceContext.ts, 9, 5))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 11, 9))
function f(c: I = class { static x = { a: "a" } }) { }
>f : Symbol(f, Decl(staticFieldWithInterfaceContext.ts, 11, 19))
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 13, 11))
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
>x : Symbol((Anonymous class).x, Decl(staticFieldWithInterfaceContext.ts, 13, 25))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 13, 38))
let { c: c3 }: { c: I } = { c: class { static x = { a: "a" } } };
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 15, 16))
>c3 : Symbol(c3, Decl(staticFieldWithInterfaceContext.ts, 15, 5))
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 15, 16))
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 15, 27))
>x : Symbol(c.x, Decl(staticFieldWithInterfaceContext.ts, 15, 38))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 15, 51))
let { c: c4 = class { static x = { a: "a" } }}: { c?: I } = { };
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 16, 49))
>c4 : Symbol(c4, Decl(staticFieldWithInterfaceContext.ts, 16, 5))
>x : Symbol(c4.x, Decl(staticFieldWithInterfaceContext.ts, 16, 21))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 16, 34))
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 16, 49))
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
let { c: c5 = class { static x = { a: "a" } }}: { c?: I } = { c: class { static x = { a: "a" } } };
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 17, 49))
>c5 : Symbol(c5, Decl(staticFieldWithInterfaceContext.ts, 17, 5))
>x : Symbol(c5.x, Decl(staticFieldWithInterfaceContext.ts, 17, 21))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 17, 34))
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 17, 49))
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 17, 61))
>x : Symbol(c.x, Decl(staticFieldWithInterfaceContext.ts, 17, 72))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 17, 85))
let [ c6 ]: [I] = [class { static x = { a: "a" } }];
>c6 : Symbol(c6, Decl(staticFieldWithInterfaceContext.ts, 18, 5))
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
>x : Symbol((Anonymous class).x, Decl(staticFieldWithInterfaceContext.ts, 18, 26))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 18, 39))
let [ c7 ]: I[] = [class { static x = { a: "a" } }];
>c7 : Symbol(c7, Decl(staticFieldWithInterfaceContext.ts, 19, 5))
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
>x : Symbol((Anonymous class).x, Decl(staticFieldWithInterfaceContext.ts, 19, 26))
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 19, 39))
// These are broken because of #40158
// let [ c8 = class { static x = { a: "a" } } ]: [I?] = [];
// let [ c9 = class { static x = { a: "a" } } ]: I[] = [];
// let [ c10 = class { static x = { a: "a" } } ]: [I?] = [class { static x = { a: "a" } }];
// let [ c11 = class { static x = { a: "a" } } ]: I[] = [class { static x = { a: "a" } }];

View File

@ -0,0 +1,122 @@
=== tests/cases/compiler/staticFieldWithInterfaceContext.ts ===
interface I {
x: { a: "a" };
>x : { a: "a"; }
>a : "a"
}
let c: I = class {
>c : I
>class { // should typecheck the same as the last line static x = { a: "a" };} : typeof c
// should typecheck the same as the last line
static x = { a: "a" };
>x : { a: "a"; }
>{ a: "a" } : { a: "a"; }
>a : "a"
>"a" : "a"
};
c.x = { a: "a" };
>c.x = { a: "a" } : { a: "a"; }
>c.x : { a: "a"; }
>c : I
>x : { a: "a"; }
>{ a: "a" } : { a: "a"; }
>a : "a"
>"a" : "a"
const ex = "x";
>ex : "x"
>"x" : "x"
let c2: I = class { static [ex] = { a: "a" }; };
>c2 : I
>class { static [ex] = { a: "a" }; } : typeof c2
>[ex] : { a: "a"; }
>ex : "x"
>{ a: "a" } : { a: "a"; }
>a : "a"
>"a" : "a"
c[ex] = { a: "a" };
>c[ex] = { a: "a" } : { a: "a"; }
>c[ex] : { a: "a"; }
>c : I
>ex : "x"
>{ a: "a" } : { a: "a"; }
>a : "a"
>"a" : "a"
function f(c: I = class { static x = { a: "a" } }) { }
>f : (c?: I) => void
>c : I
>class { static x = { a: "a" } } : typeof (Anonymous class)
>x : { a: "a"; }
>{ a: "a" } : { a: "a"; }
>a : "a"
>"a" : "a"
let { c: c3 }: { c: I } = { c: class { static x = { a: "a" } } };
>c : any
>c3 : I
>c : I
>{ c: class { static x = { a: "a" } } } : { c: typeof c; }
>c : typeof c
>class { static x = { a: "a" } } : typeof c
>x : { a: "a"; }
>{ a: "a" } : { a: "a"; }
>a : "a"
>"a" : "a"
let { c: c4 = class { static x = { a: "a" } }}: { c?: I } = { };
>c : any
>c4 : I
>class { static x = { a: "a" } } : typeof c4
>x : { a: "a"; }
>{ a: "a" } : { a: "a"; }
>a : "a"
>"a" : "a"
>c : I
>{ } : {}
let { c: c5 = class { static x = { a: "a" } }}: { c?: I } = { c: class { static x = { a: "a" } } };
>c : any
>c5 : I
>class { static x = { a: "a" } } : typeof c5
>x : { a: "a"; }
>{ a: "a" } : { a: "a"; }
>a : "a"
>"a" : "a"
>c : I
>{ c: class { static x = { a: "a" } } } : { c: typeof c; }
>c : typeof c
>class { static x = { a: "a" } } : typeof c
>x : { a: "a"; }
>{ a: "a" } : { a: "a"; }
>a : "a"
>"a" : "a"
let [ c6 ]: [I] = [class { static x = { a: "a" } }];
>c6 : I
>[class { static x = { a: "a" } }] : [typeof (Anonymous class)]
>class { static x = { a: "a" } } : typeof (Anonymous class)
>x : { a: "a"; }
>{ a: "a" } : { a: "a"; }
>a : "a"
>"a" : "a"
let [ c7 ]: I[] = [class { static x = { a: "a" } }];
>c7 : I
>[class { static x = { a: "a" } }] : (typeof (Anonymous class))[]
>class { static x = { a: "a" } } : typeof (Anonymous class)
>x : { a: "a"; }
>{ a: "a" } : { a: "a"; }
>a : "a"
>"a" : "a"
// These are broken because of #40158
// let [ c8 = class { static x = { a: "a" } } ]: [I?] = [];
// let [ c9 = class { static x = { a: "a" } } ]: I[] = [];
// let [ c10 = class { static x = { a: "a" } } ]: [I?] = [class { static x = { a: "a" } }];
// let [ c11 = class { static x = { a: "a" } } ]: I[] = [class { static x = { a: "a" } }];

View File

@ -0,0 +1,26 @@
interface I {
x: { a: "a" };
}
let c: I = class {
// should typecheck the same as the last line
static x = { a: "a" };
};
c.x = { a: "a" };
const ex = "x";
let c2: I = class { static [ex] = { a: "a" }; };
c[ex] = { a: "a" };
function f(c: I = class { static x = { a: "a" } }) { }
let { c: c3 }: { c: I } = { c: class { static x = { a: "a" } } };
let { c: c4 = class { static x = { a: "a" } }}: { c?: I } = { };
let { c: c5 = class { static x = { a: "a" } }}: { c?: I } = { c: class { static x = { a: "a" } } };
let [ c6 ]: [I] = [class { static x = { a: "a" } }];
let [ c7 ]: I[] = [class { static x = { a: "a" } }];
// These are broken because of #40158
// let [ c8 = class { static x = { a: "a" } } ]: [I?] = [];
// let [ c9 = class { static x = { a: "a" } } ]: I[] = [];
// let [ c10 = class { static x = { a: "a" } } ]: [I?] = [class { static x = { a: "a" } }];
// let [ c11 = class { static x = { a: "a" } } ]: I[] = [class { static x = { a: "a" } }];