Skip to content

Commit 0844c43

Browse files
Mark class property initializers as outside of CFA containers (#63310)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
1 parent 71586ad commit 0844c43

File tree

5 files changed

+264
-0
lines changed

5 files changed

+264
-0
lines changed

src/compiler/binder.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3869,6 +3869,9 @@ export function getContainerFlags(node: Node): ContainerFlags {
38693869
case SyntaxKind.ModuleBlock:
38703870
return ContainerFlags.IsControlFlowContainer;
38713871

3872+
case SyntaxKind.PropertyDeclaration:
3873+
return (node as PropertyDeclaration).initializer ? ContainerFlags.IsControlFlowContainer : ContainerFlags.None;
3874+
38723875
case SyntaxKind.CatchClause:
38733876
case SyntaxKind.ForStatement:
38743877
case SyntaxKind.ForInStatement:
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//// [tests/cases/compiler/classPropertyInferenceFromBroaderTypeConst.ts] ////
2+
3+
//// [classPropertyInferenceFromBroaderTypeConst.ts]
4+
// Repro from GH#62264
5+
// Class property should infer the wider declared type (AB), not the narrowed literal type ("A")
6+
7+
type AB = 'A' | 'B';
8+
9+
const DEFAULT: AB = 'A';
10+
11+
class C {
12+
D = DEFAULT;
13+
14+
method() {
15+
switch (this.D) {
16+
case 'A': break;
17+
case 'B': break; // should not error
18+
}
19+
}
20+
}
21+
22+
// D should be AB, not "A"
23+
declare const c: C;
24+
declare function expectAB(x: AB): void;
25+
expectAB(c.D); // ok
26+
c.D = 'B'; // ok
27+
28+
// Static property should work the same way
29+
class D {
30+
static SD = DEFAULT;
31+
}
32+
D.SD = 'B'; // ok
33+
34+
35+
//// [classPropertyInferenceFromBroaderTypeConst.js]
36+
"use strict";
37+
// Repro from GH#62264
38+
// Class property should infer the wider declared type (AB), not the narrowed literal type ("A")
39+
const DEFAULT = 'A';
40+
class C {
41+
D = DEFAULT;
42+
method() {
43+
switch (this.D) {
44+
case 'A': break;
45+
case 'B': break; // should not error
46+
}
47+
}
48+
}
49+
expectAB(c.D); // ok
50+
c.D = 'B'; // ok
51+
// Static property should work the same way
52+
class D {
53+
static SD = DEFAULT;
54+
}
55+
D.SD = 'B'; // ok
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//// [tests/cases/compiler/classPropertyInferenceFromBroaderTypeConst.ts] ////
2+
3+
=== classPropertyInferenceFromBroaderTypeConst.ts ===
4+
// Repro from GH#62264
5+
// Class property should infer the wider declared type (AB), not the narrowed literal type ("A")
6+
7+
type AB = 'A' | 'B';
8+
>AB : Symbol(AB, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 0, 0))
9+
10+
const DEFAULT: AB = 'A';
11+
>DEFAULT : Symbol(DEFAULT, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 5, 5))
12+
>AB : Symbol(AB, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 0, 0))
13+
14+
class C {
15+
>C : Symbol(C, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 5, 24))
16+
17+
D = DEFAULT;
18+
>D : Symbol(C.D, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 7, 9))
19+
>DEFAULT : Symbol(DEFAULT, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 5, 5))
20+
21+
method() {
22+
>method : Symbol(C.method, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 8, 16))
23+
24+
switch (this.D) {
25+
>this.D : Symbol(C.D, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 7, 9))
26+
>this : Symbol(C, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 5, 24))
27+
>D : Symbol(C.D, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 7, 9))
28+
29+
case 'A': break;
30+
case 'B': break; // should not error
31+
}
32+
}
33+
}
34+
35+
// D should be AB, not "A"
36+
declare const c: C;
37+
>c : Symbol(c, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 19, 13))
38+
>C : Symbol(C, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 5, 24))
39+
40+
declare function expectAB(x: AB): void;
41+
>expectAB : Symbol(expectAB, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 19, 19))
42+
>x : Symbol(x, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 20, 26))
43+
>AB : Symbol(AB, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 0, 0))
44+
45+
expectAB(c.D); // ok
46+
>expectAB : Symbol(expectAB, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 19, 19))
47+
>c.D : Symbol(C.D, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 7, 9))
48+
>c : Symbol(c, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 19, 13))
49+
>D : Symbol(C.D, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 7, 9))
50+
51+
c.D = 'B'; // ok
52+
>c.D : Symbol(C.D, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 7, 9))
53+
>c : Symbol(c, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 19, 13))
54+
>D : Symbol(C.D, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 7, 9))
55+
56+
// Static property should work the same way
57+
class D {
58+
>D : Symbol(D, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 22, 10))
59+
60+
static SD = DEFAULT;
61+
>SD : Symbol(D.SD, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 25, 9))
62+
>DEFAULT : Symbol(DEFAULT, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 5, 5))
63+
}
64+
D.SD = 'B'; // ok
65+
>D.SD : Symbol(D.SD, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 25, 9))
66+
>D : Symbol(D, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 22, 10))
67+
>SD : Symbol(D.SD, Decl(classPropertyInferenceFromBroaderTypeConst.ts, 25, 9))
68+
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//// [tests/cases/compiler/classPropertyInferenceFromBroaderTypeConst.ts] ////
2+
3+
=== classPropertyInferenceFromBroaderTypeConst.ts ===
4+
// Repro from GH#62264
5+
// Class property should infer the wider declared type (AB), not the narrowed literal type ("A")
6+
7+
type AB = 'A' | 'B';
8+
>AB : AB
9+
> : ^^
10+
11+
const DEFAULT: AB = 'A';
12+
>DEFAULT : AB
13+
> : ^^
14+
>'A' : "A"
15+
> : ^^^
16+
17+
class C {
18+
>C : C
19+
> : ^
20+
21+
D = DEFAULT;
22+
>D : AB
23+
> : ^^
24+
>DEFAULT : AB
25+
> : ^^
26+
27+
method() {
28+
>method : () => void
29+
> : ^^^^^^^^^^
30+
31+
switch (this.D) {
32+
>this.D : AB
33+
> : ^^
34+
>this : this
35+
> : ^^^^
36+
>D : AB
37+
> : ^^
38+
39+
case 'A': break;
40+
>'A' : "A"
41+
> : ^^^
42+
43+
case 'B': break; // should not error
44+
>'B' : "B"
45+
> : ^^^
46+
}
47+
}
48+
}
49+
50+
// D should be AB, not "A"
51+
declare const c: C;
52+
>c : C
53+
> : ^
54+
55+
declare function expectAB(x: AB): void;
56+
>expectAB : (x: AB) => void
57+
> : ^ ^^ ^^^^^
58+
>x : AB
59+
> : ^^
60+
61+
expectAB(c.D); // ok
62+
>expectAB(c.D) : void
63+
> : ^^^^
64+
>expectAB : (x: AB) => void
65+
> : ^ ^^ ^^^^^
66+
>c.D : AB
67+
> : ^^
68+
>c : C
69+
> : ^
70+
>D : AB
71+
> : ^^
72+
73+
c.D = 'B'; // ok
74+
>c.D = 'B' : "B"
75+
> : ^^^
76+
>c.D : AB
77+
> : ^^
78+
>c : C
79+
> : ^
80+
>D : AB
81+
> : ^^
82+
>'B' : "B"
83+
> : ^^^
84+
85+
// Static property should work the same way
86+
class D {
87+
>D : D
88+
> : ^
89+
90+
static SD = DEFAULT;
91+
>SD : AB
92+
> : ^^
93+
>DEFAULT : AB
94+
> : ^^
95+
}
96+
D.SD = 'B'; // ok
97+
>D.SD = 'B' : "B"
98+
> : ^^^
99+
>D.SD : AB
100+
> : ^^
101+
>D : typeof D
102+
> : ^^^^^^^^
103+
>SD : AB
104+
> : ^^
105+
>'B' : "B"
106+
> : ^^^
107+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// @strict: true
2+
3+
// Repro from GH#62264
4+
// Class property should infer the wider declared type (AB), not the narrowed literal type ("A")
5+
6+
type AB = 'A' | 'B';
7+
8+
const DEFAULT: AB = 'A';
9+
10+
class C {
11+
D = DEFAULT;
12+
13+
method() {
14+
switch (this.D) {
15+
case 'A': break;
16+
case 'B': break; // should not error
17+
}
18+
}
19+
}
20+
21+
// D should be AB, not "A"
22+
declare const c: C;
23+
declare function expectAB(x: AB): void;
24+
expectAB(c.D); // ok
25+
c.D = 'B'; // ok
26+
27+
// Static property should work the same way
28+
class D {
29+
static SD = DEFAULT;
30+
}
31+
D.SD = 'B'; // ok

0 commit comments

Comments
 (0)