Skip to content

Commit 619d485

Browse files
authored
Avoid contextually typing static properties by their own class to prevent spurious circularities (#64525)
1 parent 834e786 commit 619d485

5 files changed

Lines changed: 352 additions & 1 deletion

File tree

‎tsc/internal/checker/checker.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30100,7 +30100,8 @@ func (c *Checker) getContextualTypeForBindingElement(declaration *ast.Node, cont
3010030100

3010130101
func (c *Checker) getContextualTypeForStaticPropertyDeclaration(declaration *ast.Node, contextFlags ContextFlags) *Type {
3010230102
if ast.IsExpression(declaration.Parent) {
30103-
if parentType := c.getContextualType(declaration.Parent, contextFlags); parentType != nil {
30103+
// Don't contextually type a static property by its own class, its type might still be in-progress and that would cause spurious circularities
30104+
if parentType := c.getContextualType(declaration.Parent, contextFlags); parentType != nil && parentType.symbol != c.getSymbolOfDeclaration(declaration.Parent) {
3010430105
return c.getTypeOfPropertyOfContextualType(parentType, c.getSymbolOfDeclaration(declaration).Name)
3010530106
}
3010630107
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
classStaticPropertyNoSpuriousCircularityError1.ts(33,10): error TS7022: 'foo' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
2+
classStaticPropertyNoSpuriousCircularityError1.ts(33,25): error TS2729: Property 'foo' is used before its initialization.
3+
classStaticPropertyNoSpuriousCircularityError1.ts(37,10): error TS7022: 'foo' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
4+
classStaticPropertyNoSpuriousCircularityError1.ts(37,18): error TS2729: Property 'foo' is used before its initialization.
5+
classStaticPropertyNoSpuriousCircularityError1.ts(41,10): error TS7022: 'foo' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
6+
classStaticPropertyNoSpuriousCircularityError1.ts(41,16): error TS2448: Block-scoped variable 'Circular3' used before its declaration.
7+
classStaticPropertyNoSpuriousCircularityError1.ts(41,26): error TS2729: Property 'foo' is used before its initialization.
8+
9+
10+
==== classStaticPropertyNoSpuriousCircularityError1.ts (7 errors) ====
11+
// https://github.com/microsoft/TypeScript/issues/59271
12+
// https://github.com/microsoft/TypeScript/issues/62552
13+
14+
function id<T>(x: T): T {
15+
return x
16+
}
17+
18+
const Foo = id(class {
19+
static readonly foo = id(42) // ok
20+
})
21+
22+
Foo.foo // 42
23+
24+
const Foo2 = id(class {
25+
static foo = id(42) // ok
26+
})
27+
28+
Foo2.foo // number
29+
30+
const Foo3 = id(class {
31+
static readonly foo = [42];
32+
});
33+
34+
const Foo4 = id(
35+
class A {
36+
static foo = {};
37+
},
38+
);
39+
40+
Foo4.foo // {}
41+
42+
class Circular {
43+
static foo = Circular.foo; // error
44+
~~~
45+
!!! error TS7022: 'foo' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
46+
~~~
47+
!!! error TS2729: Property 'foo' is used before its initialization.
48+
!!! related TS2728 classStaticPropertyNoSpuriousCircularityError1.ts:33:10: 'foo' is declared here.
49+
}
50+
51+
const Circular2 = id(class C {
52+
static foo = C.foo; // error
53+
~~~
54+
!!! error TS7022: 'foo' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
55+
~~~
56+
!!! error TS2729: Property 'foo' is used before its initialization.
57+
!!! related TS2728 classStaticPropertyNoSpuriousCircularityError1.ts:37:10: 'foo' is declared here.
58+
});
59+
60+
const Circular3 = id(class {
61+
static foo = Circular3.foo; // error
62+
~~~
63+
!!! error TS7022: 'foo' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
64+
~~~~~~~~~
65+
!!! error TS2448: Block-scoped variable 'Circular3' used before its declaration.
66+
!!! related TS2728 classStaticPropertyNoSpuriousCircularityError1.ts:40:7: 'Circular3' is declared here.
67+
~~~
68+
!!! error TS2729: Property 'foo' is used before its initialization.
69+
!!! related TS2728 classStaticPropertyNoSpuriousCircularityError1.ts:41:10: 'foo' is declared here.
70+
});
71+
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
//// [tests/cases/compiler/classStaticPropertyNoSpuriousCircularityError1.ts] ////
2+
3+
=== classStaticPropertyNoSpuriousCircularityError1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/59271
5+
// https://github.com/microsoft/TypeScript/issues/62552
6+
7+
function id<T>(x: T): T {
8+
>id : Symbol(id, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 0, 0))
9+
>T : Symbol(T, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 3, 12))
10+
>x : Symbol(x, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 3, 15))
11+
>T : Symbol(T, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 3, 12))
12+
>T : Symbol(T, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 3, 12))
13+
14+
return x
15+
>x : Symbol(x, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 3, 15))
16+
}
17+
18+
const Foo = id(class {
19+
>Foo : Symbol(Foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 7, 5))
20+
>id : Symbol(id, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 0, 0))
21+
22+
static readonly foo = id(42) // ok
23+
>foo : Symbol((Anonymous class).foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 7, 22))
24+
>id : Symbol(id, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 0, 0))
25+
26+
})
27+
28+
Foo.foo // 42
29+
>Foo.foo : Symbol((Anonymous class).foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 7, 22))
30+
>Foo : Symbol(Foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 7, 5))
31+
>foo : Symbol((Anonymous class).foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 7, 22))
32+
33+
const Foo2 = id(class {
34+
>Foo2 : Symbol(Foo2, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 13, 5))
35+
>id : Symbol(id, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 0, 0))
36+
37+
static foo = id(42) // ok
38+
>foo : Symbol((Anonymous class).foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 13, 23))
39+
>id : Symbol(id, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 0, 0))
40+
41+
})
42+
43+
Foo2.foo // number
44+
>Foo2.foo : Symbol((Anonymous class).foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 13, 23))
45+
>Foo2 : Symbol(Foo2, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 13, 5))
46+
>foo : Symbol((Anonymous class).foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 13, 23))
47+
48+
const Foo3 = id(class {
49+
>Foo3 : Symbol(Foo3, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 19, 5))
50+
>id : Symbol(id, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 0, 0))
51+
52+
static readonly foo = [42];
53+
>foo : Symbol((Anonymous class).foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 19, 23))
54+
55+
});
56+
57+
const Foo4 = id(
58+
>Foo4 : Symbol(Foo4, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 23, 5))
59+
>id : Symbol(id, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 0, 0))
60+
61+
class A {
62+
>A : Symbol(A, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 23, 16))
63+
64+
static foo = {};
65+
>foo : Symbol(A.foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 24, 11))
66+
67+
},
68+
);
69+
70+
Foo4.foo // {}
71+
>Foo4.foo : Symbol(A.foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 24, 11))
72+
>Foo4 : Symbol(Foo4, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 23, 5))
73+
>foo : Symbol(A.foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 24, 11))
74+
75+
class Circular {
76+
>Circular : Symbol(Circular, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 29, 8))
77+
78+
static foo = Circular.foo; // error
79+
>foo : Symbol(Circular.foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 31, 16))
80+
>Circular.foo : Symbol(Circular.foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 31, 16))
81+
>Circular : Symbol(Circular, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 29, 8))
82+
>foo : Symbol(Circular.foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 31, 16))
83+
}
84+
85+
const Circular2 = id(class C {
86+
>Circular2 : Symbol(Circular2, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 35, 5))
87+
>id : Symbol(id, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 0, 0))
88+
>C : Symbol(C, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 35, 21))
89+
90+
static foo = C.foo; // error
91+
>foo : Symbol(C.foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 35, 30))
92+
>C.foo : Symbol(C.foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 35, 30))
93+
>C : Symbol(C, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 35, 21))
94+
>foo : Symbol(C.foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 35, 30))
95+
96+
});
97+
98+
const Circular3 = id(class {
99+
>Circular3 : Symbol(Circular3, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 39, 5))
100+
>id : Symbol(id, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 0, 0))
101+
102+
static foo = Circular3.foo; // error
103+
>foo : Symbol((Anonymous class).foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 39, 28))
104+
>Circular3.foo : Symbol((Anonymous class).foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 39, 28))
105+
>Circular3 : Symbol(Circular3, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 39, 5))
106+
>foo : Symbol((Anonymous class).foo, Decl(classStaticPropertyNoSpuriousCircularityError1.ts, 39, 28))
107+
108+
});
109+
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
//// [tests/cases/compiler/classStaticPropertyNoSpuriousCircularityError1.ts] ////
2+
3+
=== classStaticPropertyNoSpuriousCircularityError1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/59271
5+
// https://github.com/microsoft/TypeScript/issues/62552
6+
7+
function id<T>(x: T): T {
8+
>id : <T>(x: T) => T
9+
>x : T
10+
11+
return x
12+
>x : T
13+
}
14+
15+
const Foo = id(class {
16+
>Foo : typeof (Anonymous class)
17+
>id(class { static readonly foo = id(42) // ok}) : typeof (Anonymous class)
18+
>id : <T>(x: T) => T
19+
>class { static readonly foo = id(42) // ok} : typeof (Anonymous class)
20+
21+
static readonly foo = id(42) // ok
22+
>foo : 42
23+
>id(42) : 42
24+
>id : <T>(x: T) => T
25+
>42 : 42
26+
27+
})
28+
29+
Foo.foo // 42
30+
>Foo.foo : 42
31+
>Foo : typeof (Anonymous class)
32+
>foo : 42
33+
34+
const Foo2 = id(class {
35+
>Foo2 : typeof (Anonymous class)
36+
>id(class { static foo = id(42) // ok}) : typeof (Anonymous class)
37+
>id : <T>(x: T) => T
38+
>class { static foo = id(42) // ok} : typeof (Anonymous class)
39+
40+
static foo = id(42) // ok
41+
>foo : number
42+
>id(42) : 42
43+
>id : <T>(x: T) => T
44+
>42 : 42
45+
46+
})
47+
48+
Foo2.foo // number
49+
>Foo2.foo : number
50+
>Foo2 : typeof (Anonymous class)
51+
>foo : number
52+
53+
const Foo3 = id(class {
54+
>Foo3 : typeof (Anonymous class)
55+
>id(class { static readonly foo = [42];}) : typeof (Anonymous class)
56+
>id : <T>(x: T) => T
57+
>class { static readonly foo = [42];} : typeof (Anonymous class)
58+
59+
static readonly foo = [42];
60+
>foo : number[]
61+
>[42] : number[]
62+
>42 : 42
63+
64+
});
65+
66+
const Foo4 = id(
67+
>Foo4 : typeof A
68+
>id( class A { static foo = {}; },) : typeof A
69+
>id : <T>(x: T) => T
70+
71+
class A {
72+
>class A { static foo = {}; } : typeof A
73+
>A : typeof A
74+
75+
static foo = {};
76+
>foo : {}
77+
>{} : {}
78+
79+
},
80+
);
81+
82+
Foo4.foo // {}
83+
>Foo4.foo : {}
84+
>Foo4 : typeof A
85+
>foo : {}
86+
87+
class Circular {
88+
>Circular : Circular
89+
90+
static foo = Circular.foo; // error
91+
>foo : any
92+
>Circular.foo : any
93+
>Circular : typeof Circular
94+
>foo : any
95+
}
96+
97+
const Circular2 = id(class C {
98+
>Circular2 : typeof C
99+
>id(class C { static foo = C.foo; // error}) : typeof C
100+
>id : <T>(x: T) => T
101+
>class C { static foo = C.foo; // error} : typeof C
102+
>C : typeof C
103+
104+
static foo = C.foo; // error
105+
>foo : any
106+
>C.foo : any
107+
>C : typeof C
108+
>foo : any
109+
110+
});
111+
112+
const Circular3 = id(class {
113+
>Circular3 : typeof (Anonymous class)
114+
>id(class { static foo = Circular3.foo; // error}) : typeof (Anonymous class)
115+
>id : <T>(x: T) => T
116+
>class { static foo = Circular3.foo; // error} : typeof (Anonymous class)
117+
118+
static foo = Circular3.foo; // error
119+
>foo : any
120+
>Circular3.foo : any
121+
>Circular3 : typeof (Anonymous class)
122+
>foo : any
123+
124+
});
125+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/TypeScript/issues/59271
5+
// https://github.com/microsoft/TypeScript/issues/62552
6+
7+
function id<T>(x: T): T {
8+
return x
9+
}
10+
11+
const Foo = id(class {
12+
static readonly foo = id(42) // ok
13+
})
14+
15+
Foo.foo // 42
16+
17+
const Foo2 = id(class {
18+
static foo = id(42) // ok
19+
})
20+
21+
Foo2.foo // number
22+
23+
const Foo3 = id(class {
24+
static readonly foo = [42];
25+
});
26+
27+
const Foo4 = id(
28+
class A {
29+
static foo = {};
30+
},
31+
);
32+
33+
Foo4.foo // {}
34+
35+
class Circular {
36+
static foo = Circular.foo; // error
37+
}
38+
39+
const Circular2 = id(class C {
40+
static foo = C.foo; // error
41+
});
42+
43+
const Circular3 = id(class {
44+
static foo = Circular3.foo; // error
45+
});

0 commit comments

Comments
 (0)