Skip to content

Commit 7e093f0

Browse files
TypeScript Botahejlsberg
TypeScript Bot
andauthored
🤖 Pick PR #53351 (Fix subtype reduction involving typ...) into release-5.0 (#53422)
Co-authored-by: Anders Hejlsberg <[email protected]>
1 parent b345c3a commit 7e093f0

File tree

4 files changed

+261
-0
lines changed

4 files changed

+261
-0
lines changed

‎src/compiler/checker.ts

+10
Original file line numberDiff line numberDiff line change
@@ -16199,6 +16199,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1619916199
i--;
1620016200
const source = types[i];
1620116201
if (hasEmptyObject || source.flags & TypeFlags.StructuredOrInstantiable) {
16202+
// A type parameter with a union constraint may be a subtype of some union, but not a subtype of the
16203+
// individual constituents of that union. For example, `T extends A | B` is a subtype of `A | B`, but not
16204+
// a subtype of just `A` or just `B`. When we encounter such a type parameter, we therefore check if the
16205+
// type parameter is a subtype of a union of all the other types.
16206+
if (source.flags & TypeFlags.TypeParameter && getBaseConstraintOrType(source).flags & TypeFlags.Union) {
16207+
if (isTypeRelatedTo(source, getUnionType(map(types, t => t === source ? neverType : t)), strictSubtypeRelation)) {
16208+
orderedRemoveItemAt(types, i);
16209+
}
16210+
continue;
16211+
}
1620216212
// Find the first property with a unit type, if any. When constituents have a property by the same name
1620316213
// but of a different unit type, we can quickly disqualify them from subtype checks. This helps subtype
1620416214
// reduction of large discriminated union types.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
=== tests/cases/compiler/subtypeReductionUnionConstraints.ts ===
2+
// Repro from #53311
3+
4+
type FooNode = {
5+
>FooNode : Symbol(FooNode, Decl(subtypeReductionUnionConstraints.ts, 0, 0))
6+
7+
kind: 'foo';
8+
>kind : Symbol(kind, Decl(subtypeReductionUnionConstraints.ts, 2, 16))
9+
10+
children: Node[];
11+
>children : Symbol(children, Decl(subtypeReductionUnionConstraints.ts, 3, 16))
12+
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
13+
14+
};
15+
16+
type BarNode = {
17+
>BarNode : Symbol(BarNode, Decl(subtypeReductionUnionConstraints.ts, 5, 2))
18+
19+
kind: 'bar';
20+
>kind : Symbol(kind, Decl(subtypeReductionUnionConstraints.ts, 7, 16))
21+
}
22+
23+
type Node = FooNode | BarNode;
24+
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
25+
>FooNode : Symbol(FooNode, Decl(subtypeReductionUnionConstraints.ts, 0, 0))
26+
>BarNode : Symbol(BarNode, Decl(subtypeReductionUnionConstraints.ts, 5, 2))
27+
28+
type Document = {
29+
>Document : Symbol(Document, Decl(subtypeReductionUnionConstraints.ts, 11, 30))
30+
31+
kind: 'document';
32+
>kind : Symbol(kind, Decl(subtypeReductionUnionConstraints.ts, 13, 17))
33+
34+
children: Node[];
35+
>children : Symbol(children, Decl(subtypeReductionUnionConstraints.ts, 14, 21))
36+
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
37+
38+
};
39+
40+
declare function isNode(node: unknown): node is Node;
41+
>isNode : Symbol(isNode, Decl(subtypeReductionUnionConstraints.ts, 16, 2))
42+
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 18, 24))
43+
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 18, 24))
44+
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
45+
46+
declare function isBar(node: Node): node is BarNode;
47+
>isBar : Symbol(isBar, Decl(subtypeReductionUnionConstraints.ts, 18, 53))
48+
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 19, 23))
49+
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
50+
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 19, 23))
51+
>BarNode : Symbol(BarNode, Decl(subtypeReductionUnionConstraints.ts, 5, 2))
52+
53+
export function visitNodes<T extends Node>(node: Document | Node, predicate: (testNode: Node) => testNode is T): void {
54+
>visitNodes : Symbol(visitNodes, Decl(subtypeReductionUnionConstraints.ts, 19, 52))
55+
>T : Symbol(T, Decl(subtypeReductionUnionConstraints.ts, 21, 27))
56+
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
57+
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
58+
>Document : Symbol(Document, Decl(subtypeReductionUnionConstraints.ts, 11, 30))
59+
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
60+
>predicate : Symbol(predicate, Decl(subtypeReductionUnionConstraints.ts, 21, 65))
61+
>testNode : Symbol(testNode, Decl(subtypeReductionUnionConstraints.ts, 21, 78))
62+
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
63+
>testNode : Symbol(testNode, Decl(subtypeReductionUnionConstraints.ts, 21, 78))
64+
>T : Symbol(T, Decl(subtypeReductionUnionConstraints.ts, 21, 27))
65+
66+
isNode(node) && predicate(node);
67+
>isNode : Symbol(isNode, Decl(subtypeReductionUnionConstraints.ts, 16, 2))
68+
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
69+
>predicate : Symbol(predicate, Decl(subtypeReductionUnionConstraints.ts, 21, 65))
70+
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
71+
72+
if (!isNode(node) || !isBar(node)) {
73+
>isNode : Symbol(isNode, Decl(subtypeReductionUnionConstraints.ts, 16, 2))
74+
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
75+
>isBar : Symbol(isBar, Decl(subtypeReductionUnionConstraints.ts, 18, 53))
76+
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
77+
78+
const nodes: Node[] = node.children;
79+
>nodes : Symbol(nodes, Decl(subtypeReductionUnionConstraints.ts, 24, 13))
80+
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
81+
>node.children : Symbol(children, Decl(subtypeReductionUnionConstraints.ts, 3, 16), Decl(subtypeReductionUnionConstraints.ts, 14, 21))
82+
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
83+
>children : Symbol(children, Decl(subtypeReductionUnionConstraints.ts, 3, 16), Decl(subtypeReductionUnionConstraints.ts, 14, 21))
84+
}
85+
}
86+
87+
// Repro from #53311
88+
89+
type A = { a: string };
90+
>A : Symbol(A, Decl(subtypeReductionUnionConstraints.ts, 26, 1))
91+
>a : Symbol(a, Decl(subtypeReductionUnionConstraints.ts, 30, 10))
92+
93+
type B = { b: string };
94+
>B : Symbol(B, Decl(subtypeReductionUnionConstraints.ts, 30, 23))
95+
>b : Symbol(b, Decl(subtypeReductionUnionConstraints.ts, 31, 10))
96+
97+
function f1<T extends A | B>(t: T, x: A | B) {
98+
>f1 : Symbol(f1, Decl(subtypeReductionUnionConstraints.ts, 31, 23))
99+
>T : Symbol(T, Decl(subtypeReductionUnionConstraints.ts, 33, 12))
100+
>A : Symbol(A, Decl(subtypeReductionUnionConstraints.ts, 26, 1))
101+
>B : Symbol(B, Decl(subtypeReductionUnionConstraints.ts, 30, 23))
102+
>t : Symbol(t, Decl(subtypeReductionUnionConstraints.ts, 33, 29))
103+
>T : Symbol(T, Decl(subtypeReductionUnionConstraints.ts, 33, 12))
104+
>x : Symbol(x, Decl(subtypeReductionUnionConstraints.ts, 33, 34))
105+
>A : Symbol(A, Decl(subtypeReductionUnionConstraints.ts, 26, 1))
106+
>B : Symbol(B, Decl(subtypeReductionUnionConstraints.ts, 30, 23))
107+
108+
const a = [t, x]; // (A | B)[] by subtype reduction
109+
>a : Symbol(a, Decl(subtypeReductionUnionConstraints.ts, 34, 9))
110+
>t : Symbol(t, Decl(subtypeReductionUnionConstraints.ts, 33, 29))
111+
>x : Symbol(x, Decl(subtypeReductionUnionConstraints.ts, 33, 34))
112+
}
113+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
=== tests/cases/compiler/subtypeReductionUnionConstraints.ts ===
2+
// Repro from #53311
3+
4+
type FooNode = {
5+
>FooNode : { kind: 'foo'; children: Node[]; }
6+
7+
kind: 'foo';
8+
>kind : "foo"
9+
10+
children: Node[];
11+
>children : Node[]
12+
13+
};
14+
15+
type BarNode = {
16+
>BarNode : { kind: 'bar'; }
17+
18+
kind: 'bar';
19+
>kind : "bar"
20+
}
21+
22+
type Node = FooNode | BarNode;
23+
>Node : FooNode | BarNode
24+
25+
type Document = {
26+
>Document : { kind: 'document'; children: Node[]; }
27+
28+
kind: 'document';
29+
>kind : "document"
30+
31+
children: Node[];
32+
>children : Node[]
33+
34+
};
35+
36+
declare function isNode(node: unknown): node is Node;
37+
>isNode : (node: unknown) => node is Node
38+
>node : unknown
39+
40+
declare function isBar(node: Node): node is BarNode;
41+
>isBar : (node: Node) => node is BarNode
42+
>node : Node
43+
44+
export function visitNodes<T extends Node>(node: Document | Node, predicate: (testNode: Node) => testNode is T): void {
45+
>visitNodes : <T extends Node>(node: Document | Node, predicate: (testNode: Node) => testNode is T) => void
46+
>node : Node | Document
47+
>predicate : (testNode: Node) => testNode is T
48+
>testNode : Node
49+
50+
isNode(node) && predicate(node);
51+
>isNode(node) && predicate(node) : boolean
52+
>isNode(node) : boolean
53+
>isNode : (node: unknown) => node is Node
54+
>node : Node | Document
55+
>predicate(node) : boolean
56+
>predicate : (testNode: Node) => testNode is T
57+
>node : Node
58+
59+
if (!isNode(node) || !isBar(node)) {
60+
>!isNode(node) || !isBar(node) : boolean
61+
>!isNode(node) : boolean
62+
>isNode(node) : boolean
63+
>isNode : (node: unknown) => node is Node
64+
>node : Node | Document
65+
>!isBar(node) : boolean
66+
>isBar(node) : boolean
67+
>isBar : (node: Node) => node is BarNode
68+
>node : Node
69+
70+
const nodes: Node[] = node.children;
71+
>nodes : Node[]
72+
>node.children : Node[]
73+
>node : FooNode | Document
74+
>children : Node[]
75+
}
76+
}
77+
78+
// Repro from #53311
79+
80+
type A = { a: string };
81+
>A : { a: string; }
82+
>a : string
83+
84+
type B = { b: string };
85+
>B : { b: string; }
86+
>b : string
87+
88+
function f1<T extends A | B>(t: T, x: A | B) {
89+
>f1 : <T extends A | B>(t: T, x: A | B) => void
90+
>t : T
91+
>x : A | B
92+
93+
const a = [t, x]; // (A | B)[] by subtype reduction
94+
>a : (A | B)[]
95+
>[t, x] : (A | B)[]
96+
>t : T
97+
>x : A | B
98+
}
99+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// Repro from #53311
5+
6+
type FooNode = {
7+
kind: 'foo';
8+
children: Node[];
9+
};
10+
11+
type BarNode = {
12+
kind: 'bar';
13+
}
14+
15+
type Node = FooNode | BarNode;
16+
17+
type Document = {
18+
kind: 'document';
19+
children: Node[];
20+
};
21+
22+
declare function isNode(node: unknown): node is Node;
23+
declare function isBar(node: Node): node is BarNode;
24+
25+
export function visitNodes<T extends Node>(node: Document | Node, predicate: (testNode: Node) => testNode is T): void {
26+
isNode(node) && predicate(node);
27+
if (!isNode(node) || !isBar(node)) {
28+
const nodes: Node[] = node.children;
29+
}
30+
}
31+
32+
// Repro from #53311
33+
34+
type A = { a: string };
35+
type B = { b: string };
36+
37+
function f1<T extends A | B>(t: T, x: A | B) {
38+
const a = [t, x]; // (A | B)[] by subtype reduction
39+
}

0 commit comments

Comments
 (0)