Skip to content

Commit d8b191a

Browse files
committed
Improve algorithm for inferring to union types
1 parent d476552 commit d8b191a

File tree

1 file changed

+48
-3
lines changed

1 file changed

+48
-3
lines changed

src/compiler/checker.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15458,6 +15458,7 @@ namespace ts {
1545815458
let visited: Map<boolean>;
1545915459
let bivariant = false;
1546015460
let propagationType: Type;
15461+
let inferenceCount = 0;
1546115462
let allowComplexConstraintInference = true;
1546215463
inferFromTypes(originalSource, originalTarget);
1546315464

@@ -15561,6 +15562,7 @@ namespace ts {
1556115562
clearCachedInferences(inferences);
1556215563
}
1556315564
}
15565+
inferenceCount++;
1556415566
return;
1556515567
}
1556615568
else {
@@ -15610,13 +15612,16 @@ namespace ts {
1561015612
inferFromTypes(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target));
1561115613
inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target));
1561215614
}
15615+
else if (target.flags & TypeFlags.Union) {
15616+
inferToUnionType(source, <UnionType>target);
15617+
}
15618+
else if (target.flags & TypeFlags.Intersection) {
15619+
inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, /*isIntersection*/ true);
15620+
}
1561315621
else if (target.flags & TypeFlags.Conditional && !contravariant) {
1561415622
const targetTypes = [getTrueTypeFromConditionalType(<ConditionalType>target), getFalseTypeFromConditionalType(<ConditionalType>target)];
1561515623
inferToMultipleTypes(source, targetTypes, /*isIntersection*/ false);
1561615624
}
15617-
else if (target.flags & TypeFlags.UnionOrIntersection) {
15618-
inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, !!(target.flags & TypeFlags.Intersection));
15619-
}
1562015625
else if (source.flags & TypeFlags.Union) {
1562115626
// Source is a union or intersection type, infer from each constituent type
1562215627
const sourceTypes = (<UnionOrIntersectionType>source).types;
@@ -15742,6 +15747,46 @@ namespace ts {
1574215747
}
1574315748
}
1574415749

15750+
function inferToUnionType(source: Type, target: UnionType) {
15751+
const sources = source.flags & TypeFlags.Union ? (<UnionType>source).types : [source];
15752+
const matched = new Array<boolean>(sources.length);
15753+
let typeVariableCount = 0;
15754+
// First infer to types that are not naked type variables. For each source type we
15755+
// track whether inferences were made from that particular type to some target.
15756+
for (const t of target.types) {
15757+
if (getInferenceInfoForType(t)) {
15758+
typeVariableCount++;
15759+
}
15760+
else {
15761+
for (let i = 0; i < sources.length; i++) {
15762+
const count = inferenceCount;
15763+
inferFromTypes(sources[i], t);
15764+
if (count !== inferenceCount) matched[i] = true;
15765+
}
15766+
}
15767+
}
15768+
// If there are naked type variables in the target, create a union of the source types
15769+
// from which no inferences have been made so far and infer from that union to each naked
15770+
// type variable. If there is more than one naked type variable, give lower priority to
15771+
// the inferences as they are less specific.
15772+
if (typeVariableCount > 0) {
15773+
const unmatched = flatMap(sources, (s, i) => matched![i] ? undefined : s);
15774+
if (unmatched.length) {
15775+
const s = getUnionType(unmatched);
15776+
const savePriority = priority;
15777+
if (typeVariableCount > 1) {
15778+
priority |= InferencePriority.NakedTypeVariable;
15779+
}
15780+
for (const t of target.types) {
15781+
if (getInferenceInfoForType(t)) {
15782+
inferFromTypes(s, t);
15783+
}
15784+
}
15785+
priority = savePriority;
15786+
}
15787+
}
15788+
}
15789+
1574515790
function inferToMappedType(source: Type, target: MappedType, constraintType: Type): boolean {
1574615791
if (constraintType.flags & TypeFlags.Union) {
1574715792
let result = false;

0 commit comments

Comments
 (0)