Π·Π΅ΡΠΊΠ°Π»ΠΎ ΠΈΠ· https://github.com/microsoft/TypeScript.git
Co-authored-by: Anders Hejlsberg <andersh@microsoft.com>
This commit is contained in:
Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ
b345c3a563
ΠΠΎΠΌΠΌΠΈΡ
7e093f072b
|
@ -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
|
||||
}
|
ΠΠ°Π³ΡΡΠ·ΠΊΠ°β¦
Π‘ΡΡΠ»ΠΊΠ° Π² Π½ΠΎΠ²ΠΎΠΉ Π·Π°Π΄Π°ΡΠ΅