Skip to content

Commit

Permalink
Add missing contextual type to static PropertyDeclarations
Browse files Browse the repository at this point in the history
Makes it possible to type class static fields.

Fixes #33897.
  • Loading branch information
elibarzilay committed Aug 20, 2020
1 parent 598e9b2 commit f9e360d
Show file tree
Hide file tree
Showing 8 changed files with 390 additions and 38 deletions.
12 changes: 11 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Expand Down Expand Up @@ -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";
}
}
Expand All @@ -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";
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
}
Expand All @@ -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))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
Expand All @@ -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"
}
}
Expand Down
106 changes: 106 additions & 0 deletions tests/baselines/reference/staticFieldWithInterfaceContext.js
Original file line number Diff line number Diff line change
@@ -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" } }];
92 changes: 92 additions & 0 deletions tests/baselines/reference/staticFieldWithInterfaceContext.symbols
Original file line number Diff line number Diff line change
@@ -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" } }];

0 comments on commit f9e360d

Please sign in to comment.