Skip to content

Commit f9e360d

Browse files
committed
Add missing contextual type to static PropertyDeclarations
Makes it possible to type class static fields. Fixes #33897.
1 parent 598e9b2 commit f9e360d

8 files changed

+390
-38
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22983,7 +22983,11 @@ namespace ts {
2298322983
return getContextuallyTypedParameterType(declaration);
2298422984
case SyntaxKind.BindingElement:
2298522985
return getContextualTypeForBindingElement(declaration);
22986-
// By default, do nothing and return undefined - only parameters and binding elements have context implied by a parent
22986+
case SyntaxKind.PropertyDeclaration:
22987+
if (hasSyntacticModifier(declaration, ModifierFlags.Static)) {
22988+
return getContextualTypeForStaticPropertyDeclaration(declaration);
22989+
}
22990+
// By default, do nothing and return undefined - only the above cases have context implied by a parent
2298722991
}
2298822992
}
2298922993

@@ -23001,6 +23005,12 @@ namespace ts {
2300123005
}
2300223006
}
2300323007

23008+
function getContextualTypeForStaticPropertyDeclaration(declaration: PropertyDeclaration): Type | undefined {
23009+
const parentType = isExpression(declaration.parent) && getContextualType(declaration.parent);
23010+
if (!parentType) return undefined;
23011+
return getTypeOfPropertyOfContextualType(parentType, getSymbolOfNode(declaration).escapedName);
23012+
}
23013+
2300423014
// In a variable, parameter or property declaration with a type annotation,
2300523015
// the contextual type of an initializer expression is the type of the variable, parameter or property.
2300623016
// Otherwise, in a parameter declaration of a contextually typed function expression,

tests/baselines/reference/contextuallyTypedClassExpressionMethodDeclaration01.errors.txt

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(16,24): error TS7006: Parameter 'arg' implicitly has an 'any' type.
22
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(19,24): error TS7006: Parameter 'arg' implicitly has an 'any' type.
3-
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(27,27): error TS7006: Parameter 'arg' implicitly has an 'any' type.
4-
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(30,27): error TS7006: Parameter 'arg' implicitly has an 'any' type.
5-
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(38,36): error TS7006: Parameter 'arg' implicitly has an 'any' type.
6-
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts(41,36): error TS7006: Parameter 'arg' implicitly has an 'any' type.
73

84

9-
==== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts (6 errors) ====
5+
==== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedClassExpressionMethodDeclaration01.ts (2 errors) ====
106
interface A {
117
numProp: number;
128
}
@@ -38,13 +34,9 @@ tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTyp
3834
function getFoo2(): Foo {
3935
return class {
4036
static method1 = (arg) => {
41-
~~~
42-
!!! error TS7006: Parameter 'arg' implicitly has an 'any' type.
4337
arg.numProp = 10;
4438
}
4539
static method2 = (arg) => {
46-
~~~
47-
!!! error TS7006: Parameter 'arg' implicitly has an 'any' type.
4840
arg.strProp = "hello";
4941
}
5042
}
@@ -53,13 +45,9 @@ tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTyp
5345
function getFoo3(): Foo {
5446
return class {
5547
static method1 = function (arg) {
56-
~~~
57-
!!! error TS7006: Parameter 'arg' implicitly has an 'any' type.
5848
arg.numProp = 10;
5949
}
6050
static method2 = function (arg) {
61-
~~~
62-
!!! error TS7006: Parameter 'arg' implicitly has an 'any' type.
6351
arg.strProp = "hello";
6452
}
6553
}

tests/baselines/reference/contextuallyTypedClassExpressionMethodDeclaration01.symbols

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,18 @@ function getFoo2(): Foo {
5959
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 26, 26))
6060

6161
arg.numProp = 10;
62+
>arg.numProp : Symbol(A.numProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 0, 13))
6263
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 26, 26))
64+
>numProp : Symbol(A.numProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 0, 13))
6365
}
6466
static method2 = (arg) => {
6567
>method2 : Symbol((Anonymous class).method2, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 28, 9))
6668
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 29, 26))
6769

6870
arg.strProp = "hello";
71+
>arg.strProp : Symbol(B.strProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 4, 14))
6972
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 29, 26))
73+
>strProp : Symbol(B.strProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 4, 14))
7074
}
7175
}
7276
}
@@ -81,14 +85,18 @@ function getFoo3(): Foo {
8185
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 37, 35))
8286

8387
arg.numProp = 10;
88+
>arg.numProp : Symbol(A.numProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 0, 13))
8489
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 37, 35))
90+
>numProp : Symbol(A.numProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 0, 13))
8591
}
8692
static method2 = function (arg) {
8793
>method2 : Symbol((Anonymous class).method2, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 39, 9))
8894
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 40, 35))
8995

9096
arg.strProp = "hello";
97+
>arg.strProp : Symbol(B.strProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 4, 14))
9198
>arg : Symbol(arg, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 40, 35))
99+
>strProp : Symbol(B.strProp, Decl(contextuallyTypedClassExpressionMethodDeclaration01.ts, 4, 14))
92100
}
93101
}
94102
}

tests/baselines/reference/contextuallyTypedClassExpressionMethodDeclaration01.types

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -57,27 +57,27 @@ function getFoo2(): Foo {
5757
>class { static method1 = (arg) => { arg.numProp = 10; } static method2 = (arg) => { arg.strProp = "hello"; } } : typeof (Anonymous class)
5858

5959
static method1 = (arg) => {
60-
>method1 : (arg: any) => void
61-
>(arg) => { arg.numProp = 10; } : (arg: any) => void
62-
>arg : any
60+
>method1 : (arg: A) => void
61+
>(arg) => { arg.numProp = 10; } : (arg: A) => void
62+
>arg : A
6363

6464
arg.numProp = 10;
6565
>arg.numProp = 10 : 10
66-
>arg.numProp : any
67-
>arg : any
68-
>numProp : any
66+
>arg.numProp : number
67+
>arg : A
68+
>numProp : number
6969
>10 : 10
7070
}
7171
static method2 = (arg) => {
72-
>method2 : (arg: any) => void
73-
>(arg) => { arg.strProp = "hello"; } : (arg: any) => void
74-
>arg : any
72+
>method2 : (arg: B) => void
73+
>(arg) => { arg.strProp = "hello"; } : (arg: B) => void
74+
>arg : B
7575

7676
arg.strProp = "hello";
7777
>arg.strProp = "hello" : "hello"
78-
>arg.strProp : any
79-
>arg : any
80-
>strProp : any
78+
>arg.strProp : string
79+
>arg : B
80+
>strProp : string
8181
>"hello" : "hello"
8282
}
8383
}
@@ -90,27 +90,27 @@ function getFoo3(): Foo {
9090
>class { static method1 = function (arg) { arg.numProp = 10; } static method2 = function (arg) { arg.strProp = "hello"; } } : typeof (Anonymous class)
9191

9292
static method1 = function (arg) {
93-
>method1 : (arg: any) => void
94-
>function (arg) { arg.numProp = 10; } : (arg: any) => void
95-
>arg : any
93+
>method1 : (arg: A) => void
94+
>function (arg) { arg.numProp = 10; } : (arg: A) => void
95+
>arg : A
9696

9797
arg.numProp = 10;
9898
>arg.numProp = 10 : 10
99-
>arg.numProp : any
100-
>arg : any
101-
>numProp : any
99+
>arg.numProp : number
100+
>arg : A
101+
>numProp : number
102102
>10 : 10
103103
}
104104
static method2 = function (arg) {
105-
>method2 : (arg: any) => void
106-
>function (arg) { arg.strProp = "hello"; } : (arg: any) => void
107-
>arg : any
105+
>method2 : (arg: B) => void
106+
>function (arg) { arg.strProp = "hello"; } : (arg: B) => void
107+
>arg : B
108108

109109
arg.strProp = "hello";
110110
>arg.strProp = "hello" : "hello"
111-
>arg.strProp : any
112-
>arg : any
113-
>strProp : any
111+
>arg.strProp : string
112+
>arg : B
113+
>strProp : string
114114
>"hello" : "hello"
115115
}
116116
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//// [staticFieldWithInterfaceContext.ts]
2+
interface I {
3+
x: { a: "a" };
4+
}
5+
let c: I = class {
6+
// should typecheck the same as the last line
7+
static x = { a: "a" };
8+
};
9+
c.x = { a: "a" };
10+
11+
const ex = "x";
12+
let c2: I = class { static [ex] = { a: "a" }; };
13+
c[ex] = { a: "a" };
14+
15+
function f(c: I = class { static x = { a: "a" } }) { }
16+
17+
let { c: c3 }: { c: I } = { c: class { static x = { a: "a" } } };
18+
let { c: c4 = class { static x = { a: "a" } }}: { c?: I } = { };
19+
let { c: c5 = class { static x = { a: "a" } }}: { c?: I } = { c: class { static x = { a: "a" } } };
20+
let [ c6 ]: [I] = [class { static x = { a: "a" } }];
21+
let [ c7 ]: I[] = [class { static x = { a: "a" } }];
22+
23+
// These are broken because of #40158
24+
// let [ c8 = class { static x = { a: "a" } } ]: [I?] = [];
25+
// let [ c9 = class { static x = { a: "a" } } ]: I[] = [];
26+
// let [ c10 = class { static x = { a: "a" } } ]: [I?] = [class { static x = { a: "a" } }];
27+
// let [ c11 = class { static x = { a: "a" } } ]: I[] = [class { static x = { a: "a" } }];
28+
29+
30+
//// [staticFieldWithInterfaceContext.js]
31+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
32+
var c = (_a = /** @class */ (function () {
33+
function class_1() {
34+
}
35+
return class_1;
36+
}()),
37+
// should typecheck the same as the last line
38+
_a.x = { a: "a" },
39+
_a);
40+
c.x = { a: "a" };
41+
var ex = "x";
42+
var c2 = (_c = /** @class */ (function () {
43+
function class_2() {
44+
}
45+
return class_2;
46+
}()),
47+
_b = ex,
48+
_c[_b] = { a: "a" },
49+
_c);
50+
c[ex] = { a: "a" };
51+
function f(c) {
52+
var _a;
53+
if (c === void 0) { c = (_a = /** @class */ (function () {
54+
function class_3() {
55+
}
56+
return class_3;
57+
}()),
58+
_a.x = { a: "a" },
59+
_a); }
60+
}
61+
var c3 = { c: (_d = /** @class */ (function () {
62+
function class_4() {
63+
}
64+
return class_4;
65+
}()),
66+
_d.x = { a: "a" },
67+
_d) }.c;
68+
var _k = {}.c, c4 = _k === void 0 ? (_e = /** @class */ (function () {
69+
function class_5() {
70+
}
71+
return class_5;
72+
}()),
73+
_e.x = { a: "a" },
74+
_e) : _k;
75+
var _l = { c: (_g = /** @class */ (function () {
76+
function class_6() {
77+
}
78+
return class_6;
79+
}()),
80+
_g.x = { a: "a" },
81+
_g) }.c, c5 = _l === void 0 ? (_f = /** @class */ (function () {
82+
function class_7() {
83+
}
84+
return class_7;
85+
}()),
86+
_f.x = { a: "a" },
87+
_f) : _l;
88+
var c6 = [(_h = /** @class */ (function () {
89+
function class_8() {
90+
}
91+
return class_8;
92+
}()),
93+
_h.x = { a: "a" },
94+
_h)][0];
95+
var c7 = [(_j = /** @class */ (function () {
96+
function class_9() {
97+
}
98+
return class_9;
99+
}()),
100+
_j.x = { a: "a" },
101+
_j)][0];
102+
// These are broken because of #40158
103+
// let [ c8 = class { static x = { a: "a" } } ]: [I?] = [];
104+
// let [ c9 = class { static x = { a: "a" } } ]: I[] = [];
105+
// let [ c10 = class { static x = { a: "a" } } ]: [I?] = [class { static x = { a: "a" } }];
106+
// let [ c11 = class { static x = { a: "a" } } ]: I[] = [class { static x = { a: "a" } }];
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
=== tests/cases/compiler/staticFieldWithInterfaceContext.ts ===
2+
interface I {
3+
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
4+
5+
x: { a: "a" };
6+
>x : Symbol(I.x, Decl(staticFieldWithInterfaceContext.ts, 0, 13))
7+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 1, 8))
8+
}
9+
let c: I = class {
10+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 3, 3))
11+
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
12+
13+
// should typecheck the same as the last line
14+
static x = { a: "a" };
15+
>x : Symbol(c.x, Decl(staticFieldWithInterfaceContext.ts, 3, 18))
16+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 5, 16))
17+
18+
};
19+
c.x = { a: "a" };
20+
>c.x : Symbol(I.x, Decl(staticFieldWithInterfaceContext.ts, 0, 13))
21+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 3, 3))
22+
>x : Symbol(I.x, Decl(staticFieldWithInterfaceContext.ts, 0, 13))
23+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 7, 7))
24+
25+
const ex = "x";
26+
>ex : Symbol(ex, Decl(staticFieldWithInterfaceContext.ts, 9, 5))
27+
28+
let c2: I = class { static [ex] = { a: "a" }; };
29+
>c2 : Symbol(c2, Decl(staticFieldWithInterfaceContext.ts, 10, 3))
30+
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
31+
>[ex] : Symbol(c2[ex], Decl(staticFieldWithInterfaceContext.ts, 10, 19))
32+
>ex : Symbol(ex, Decl(staticFieldWithInterfaceContext.ts, 9, 5))
33+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 10, 35))
34+
35+
c[ex] = { a: "a" };
36+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 3, 3))
37+
>ex : Symbol(ex, Decl(staticFieldWithInterfaceContext.ts, 9, 5))
38+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 11, 9))
39+
40+
function f(c: I = class { static x = { a: "a" } }) { }
41+
>f : Symbol(f, Decl(staticFieldWithInterfaceContext.ts, 11, 19))
42+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 13, 11))
43+
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
44+
>x : Symbol((Anonymous class).x, Decl(staticFieldWithInterfaceContext.ts, 13, 25))
45+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 13, 38))
46+
47+
let { c: c3 }: { c: I } = { c: class { static x = { a: "a" } } };
48+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 15, 16))
49+
>c3 : Symbol(c3, Decl(staticFieldWithInterfaceContext.ts, 15, 5))
50+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 15, 16))
51+
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
52+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 15, 27))
53+
>x : Symbol(c.x, Decl(staticFieldWithInterfaceContext.ts, 15, 38))
54+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 15, 51))
55+
56+
let { c: c4 = class { static x = { a: "a" } }}: { c?: I } = { };
57+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 16, 49))
58+
>c4 : Symbol(c4, Decl(staticFieldWithInterfaceContext.ts, 16, 5))
59+
>x : Symbol(c4.x, Decl(staticFieldWithInterfaceContext.ts, 16, 21))
60+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 16, 34))
61+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 16, 49))
62+
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
63+
64+
let { c: c5 = class { static x = { a: "a" } }}: { c?: I } = { c: class { static x = { a: "a" } } };
65+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 17, 49))
66+
>c5 : Symbol(c5, Decl(staticFieldWithInterfaceContext.ts, 17, 5))
67+
>x : Symbol(c5.x, Decl(staticFieldWithInterfaceContext.ts, 17, 21))
68+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 17, 34))
69+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 17, 49))
70+
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
71+
>c : Symbol(c, Decl(staticFieldWithInterfaceContext.ts, 17, 61))
72+
>x : Symbol(c.x, Decl(staticFieldWithInterfaceContext.ts, 17, 72))
73+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 17, 85))
74+
75+
let [ c6 ]: [I] = [class { static x = { a: "a" } }];
76+
>c6 : Symbol(c6, Decl(staticFieldWithInterfaceContext.ts, 18, 5))
77+
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
78+
>x : Symbol((Anonymous class).x, Decl(staticFieldWithInterfaceContext.ts, 18, 26))
79+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 18, 39))
80+
81+
let [ c7 ]: I[] = [class { static x = { a: "a" } }];
82+
>c7 : Symbol(c7, Decl(staticFieldWithInterfaceContext.ts, 19, 5))
83+
>I : Symbol(I, Decl(staticFieldWithInterfaceContext.ts, 0, 0))
84+
>x : Symbol((Anonymous class).x, Decl(staticFieldWithInterfaceContext.ts, 19, 26))
85+
>a : Symbol(a, Decl(staticFieldWithInterfaceContext.ts, 19, 39))
86+
87+
// These are broken because of #40158
88+
// let [ c8 = class { static x = { a: "a" } } ]: [I?] = [];
89+
// let [ c9 = class { static x = { a: "a" } } ]: I[] = [];
90+
// let [ c10 = class { static x = { a: "a" } } ]: [I?] = [class { static x = { a: "a" } }];
91+
// let [ c11 = class { static x = { a: "a" } } ]: I[] = [class { static x = { a: "a" } }];
92+

0 commit comments

Comments
 (0)