πŸ€– Pick PR #53351 (Fix subtype reduction involving typ...) into release-5.0 (#53422)

Co-authored-by: Anders Hejlsberg <andersh@microsoft.com>
This commit is contained in:
TypeScript Bot 2023-03-28 10:02:37 -07:00 ΠΊΠΎΠΌΠΌΠΈΡ‚ ΠΏΡ€ΠΎΠΈΠ·Π²Ρ‘Π» GitHub
Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒ b345c3a563
ΠšΠΎΠΌΠΌΠΈΡ‚ 7e093f072b
НС Π½Π°ΠΉΠ΄Π΅Π½ ΠΊΠ»ΡŽΡ‡, ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΉ Π΄Π°Π½Π½ΠΎΠΉ подписи
Π˜Π΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΠΊΠ»ΡŽΡ‡Π° GPG: 4AEE18F83AFDEB23
4 ΠΈΠ·ΠΌΠ΅Π½Ρ‘Π½Π½Ρ‹Ρ… Ρ„Π°ΠΉΠ»ΠΎΠ²: 261 Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΉ ΠΈ 0 ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠΉ

ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ„Π°ΠΉΠ»

@ -16199,6 +16199,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
i--;
const source = types[i];
if (hasEmptyObject || source.flags & TypeFlags.StructuredOrInstantiable) {
// A type parameter with a union constraint may be a subtype of some union, but not a subtype of the
// individual constituents of that union. For example, `T extends A | B` is a subtype of `A | B`, but not
// a subtype of just `A` or just `B`. When we encounter such a type parameter, we therefore check if the
// type parameter is a subtype of a union of all the other types.
if (source.flags & TypeFlags.TypeParameter && getBaseConstraintOrType(source).flags & TypeFlags.Union) {
if (isTypeRelatedTo(source, getUnionType(map(types, t => t === source ? neverType : t)), strictSubtypeRelation)) {
orderedRemoveItemAt(types, i);
}
continue;
}
// Find the first property with a unit type, if any. When constituents have a property by the same name
// but of a different unit type, we can quickly disqualify them from subtype checks. This helps subtype
// reduction of large discriminated union types.

ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ„Π°ΠΉΠ»

@ -0,0 +1,113 @@
=== tests/cases/compiler/subtypeReductionUnionConstraints.ts ===
// Repro from #53311
type FooNode = {
>FooNode : Symbol(FooNode, Decl(subtypeReductionUnionConstraints.ts, 0, 0))
kind: 'foo';
>kind : Symbol(kind, Decl(subtypeReductionUnionConstraints.ts, 2, 16))
children: Node[];
>children : Symbol(children, Decl(subtypeReductionUnionConstraints.ts, 3, 16))
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
};
type BarNode = {
>BarNode : Symbol(BarNode, Decl(subtypeReductionUnionConstraints.ts, 5, 2))
kind: 'bar';
>kind : Symbol(kind, Decl(subtypeReductionUnionConstraints.ts, 7, 16))
}
type Node = FooNode | BarNode;
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
>FooNode : Symbol(FooNode, Decl(subtypeReductionUnionConstraints.ts, 0, 0))
>BarNode : Symbol(BarNode, Decl(subtypeReductionUnionConstraints.ts, 5, 2))
type Document = {
>Document : Symbol(Document, Decl(subtypeReductionUnionConstraints.ts, 11, 30))
kind: 'document';
>kind : Symbol(kind, Decl(subtypeReductionUnionConstraints.ts, 13, 17))
children: Node[];
>children : Symbol(children, Decl(subtypeReductionUnionConstraints.ts, 14, 21))
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
};
declare function isNode(node: unknown): node is Node;
>isNode : Symbol(isNode, Decl(subtypeReductionUnionConstraints.ts, 16, 2))
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 18, 24))
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 18, 24))
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
declare function isBar(node: Node): node is BarNode;
>isBar : Symbol(isBar, Decl(subtypeReductionUnionConstraints.ts, 18, 53))
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 19, 23))
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 19, 23))
>BarNode : Symbol(BarNode, Decl(subtypeReductionUnionConstraints.ts, 5, 2))
export function visitNodes<T extends Node>(node: Document | Node, predicate: (testNode: Node) => testNode is T): void {
>visitNodes : Symbol(visitNodes, Decl(subtypeReductionUnionConstraints.ts, 19, 52))
>T : Symbol(T, Decl(subtypeReductionUnionConstraints.ts, 21, 27))
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
>Document : Symbol(Document, Decl(subtypeReductionUnionConstraints.ts, 11, 30))
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
>predicate : Symbol(predicate, Decl(subtypeReductionUnionConstraints.ts, 21, 65))
>testNode : Symbol(testNode, Decl(subtypeReductionUnionConstraints.ts, 21, 78))
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
>testNode : Symbol(testNode, Decl(subtypeReductionUnionConstraints.ts, 21, 78))
>T : Symbol(T, Decl(subtypeReductionUnionConstraints.ts, 21, 27))
isNode(node) && predicate(node);
>isNode : Symbol(isNode, Decl(subtypeReductionUnionConstraints.ts, 16, 2))
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
>predicate : Symbol(predicate, Decl(subtypeReductionUnionConstraints.ts, 21, 65))
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
if (!isNode(node) || !isBar(node)) {
>isNode : Symbol(isNode, Decl(subtypeReductionUnionConstraints.ts, 16, 2))
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
>isBar : Symbol(isBar, Decl(subtypeReductionUnionConstraints.ts, 18, 53))
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
const nodes: Node[] = node.children;
>nodes : Symbol(nodes, Decl(subtypeReductionUnionConstraints.ts, 24, 13))
>Node : Symbol(Node, Decl(subtypeReductionUnionConstraints.ts, 9, 1))
>node.children : Symbol(children, Decl(subtypeReductionUnionConstraints.ts, 3, 16), Decl(subtypeReductionUnionConstraints.ts, 14, 21))
>node : Symbol(node, Decl(subtypeReductionUnionConstraints.ts, 21, 43))
>children : Symbol(children, Decl(subtypeReductionUnionConstraints.ts, 3, 16), Decl(subtypeReductionUnionConstraints.ts, 14, 21))
}
}
// Repro from #53311
type A = { a: string };
>A : Symbol(A, Decl(subtypeReductionUnionConstraints.ts, 26, 1))
>a : Symbol(a, Decl(subtypeReductionUnionConstraints.ts, 30, 10))
type B = { b: string };
>B : Symbol(B, Decl(subtypeReductionUnionConstraints.ts, 30, 23))
>b : Symbol(b, Decl(subtypeReductionUnionConstraints.ts, 31, 10))
function f1<T extends A | B>(t: T, x: A | B) {
>f1 : Symbol(f1, Decl(subtypeReductionUnionConstraints.ts, 31, 23))
>T : Symbol(T, Decl(subtypeReductionUnionConstraints.ts, 33, 12))
>A : Symbol(A, Decl(subtypeReductionUnionConstraints.ts, 26, 1))
>B : Symbol(B, Decl(subtypeReductionUnionConstraints.ts, 30, 23))
>t : Symbol(t, Decl(subtypeReductionUnionConstraints.ts, 33, 29))
>T : Symbol(T, Decl(subtypeReductionUnionConstraints.ts, 33, 12))
>x : Symbol(x, Decl(subtypeReductionUnionConstraints.ts, 33, 34))
>A : Symbol(A, Decl(subtypeReductionUnionConstraints.ts, 26, 1))
>B : Symbol(B, Decl(subtypeReductionUnionConstraints.ts, 30, 23))
const a = [t, x]; // (A | B)[] by subtype reduction
>a : Symbol(a, Decl(subtypeReductionUnionConstraints.ts, 34, 9))
>t : Symbol(t, Decl(subtypeReductionUnionConstraints.ts, 33, 29))
>x : Symbol(x, Decl(subtypeReductionUnionConstraints.ts, 33, 34))
}

ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ„Π°ΠΉΠ»

@ -0,0 +1,99 @@
=== tests/cases/compiler/subtypeReductionUnionConstraints.ts ===
// Repro from #53311
type FooNode = {
>FooNode : { kind: 'foo'; children: Node[]; }
kind: 'foo';
>kind : "foo"
children: Node[];
>children : Node[]
};
type BarNode = {
>BarNode : { kind: 'bar'; }
kind: 'bar';
>kind : "bar"
}
type Node = FooNode | BarNode;
>Node : FooNode | BarNode
type Document = {
>Document : { kind: 'document'; children: Node[]; }
kind: 'document';
>kind : "document"
children: Node[];
>children : Node[]
};
declare function isNode(node: unknown): node is Node;
>isNode : (node: unknown) => node is Node
>node : unknown
declare function isBar(node: Node): node is BarNode;
>isBar : (node: Node) => node is BarNode
>node : Node
export function visitNodes<T extends Node>(node: Document | Node, predicate: (testNode: Node) => testNode is T): void {
>visitNodes : <T extends Node>(node: Document | Node, predicate: (testNode: Node) => testNode is T) => void
>node : Node | Document
>predicate : (testNode: Node) => testNode is T
>testNode : Node
isNode(node) && predicate(node);
>isNode(node) && predicate(node) : boolean
>isNode(node) : boolean
>isNode : (node: unknown) => node is Node
>node : Node | Document
>predicate(node) : boolean
>predicate : (testNode: Node) => testNode is T
>node : Node
if (!isNode(node) || !isBar(node)) {
>!isNode(node) || !isBar(node) : boolean
>!isNode(node) : boolean
>isNode(node) : boolean
>isNode : (node: unknown) => node is Node
>node : Node | Document
>!isBar(node) : boolean
>isBar(node) : boolean
>isBar : (node: Node) => node is BarNode
>node : Node
const nodes: Node[] = node.children;
>nodes : Node[]
>node.children : Node[]
>node : FooNode | Document
>children : Node[]
}
}
// Repro from #53311
type A = { a: string };
>A : { a: string; }
>a : string
type B = { b: string };
>B : { b: string; }
>b : string
function f1<T extends A | B>(t: T, x: A | B) {
>f1 : <T extends A | B>(t: T, x: A | B) => void
>t : T
>x : A | B
const a = [t, x]; // (A | B)[] by subtype reduction
>a : (A | B)[]
>[t, x] : (A | B)[]
>t : T
>x : A | B
}

ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ„Π°ΠΉΠ»

@ -0,0 +1,39 @@
// @strict: true
// @noEmit: true
// Repro from #53311
type FooNode = {
kind: 'foo';
children: Node[];
};
type BarNode = {
kind: 'bar';
}
type Node = FooNode | BarNode;
type Document = {
kind: 'document';
children: Node[];
};
declare function isNode(node: unknown): node is Node;
declare function isBar(node: Node): node is BarNode;
export function visitNodes<T extends Node>(node: Document | Node, predicate: (testNode: Node) => testNode is T): void {
isNode(node) && predicate(node);
if (!isNode(node) || !isBar(node)) {
const nodes: Node[] = node.children;
}
}
// Repro from #53311
type A = { a: string };
type B = { b: string };
function f1<T extends A | B>(t: T, x: A | B) {
const a = [t, x]; // (A | B)[] by subtype reduction
}