Skip to content

Commit e6259a5

Browse files
committed
Fix union/union and intersection/intersection type inference
1 parent ee9c4dc commit e6259a5

File tree

1 file changed

+28
-31
lines changed

1 file changed

+28
-31
lines changed

src/compiler/checker.ts

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6087,14 +6087,23 @@ namespace ts {
60876087
function inferFromTypes(source: Type, target: Type) {
60886088
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
60896089
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
6090-
// Source and target are both unions or both intersections. To improve the quality of
6091-
// inferences we first reduce the types by removing constituents that are identically
6092-
// matched by a constituent in the other type. For example, when inferring from
6093-
// 'string | string[]' to 'string | T', we reduce the types to 'string[]' and 'T'.
6094-
const reducedSource = reduceUnionOrIntersectionType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target);
6095-
const reducedTarget = reduceUnionOrIntersectionType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source);
6096-
source = reducedSource;
6097-
target = reducedTarget;
6090+
// Source and target are both unions or both intersections. First, find each
6091+
// target constituent type that has an identically matching source constituent
6092+
// type, and for each such target constituent type, infer from the type to itself.
6093+
let matchingTypes: Type[];
6094+
for (const t of (<UnionOrIntersectionType>target).types) {
6095+
if (typeIdenticalToSomeType(t, (<UnionOrIntersectionType>source).types)) {
6096+
(matchingTypes || (matchingTypes = [])).push(t);
6097+
inferFromTypes(t, t);
6098+
}
6099+
}
6100+
// To improve the quality of inferences, reduce the source and target types by
6101+
// removing the identically matched constituents. For example, when inferring from
6102+
// 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'.
6103+
if (matchingTypes) {
6104+
source = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>source, matchingTypes);
6105+
target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
6106+
}
60986107
}
60996108
if (target.flags & TypeFlags.TypeParameter) {
61006109
// If target is a type parameter, make an inference, unless the source type contains
@@ -6256,39 +6265,27 @@ namespace ts {
62566265
}
62576266
}
62586267

6259-
function typeIdenticalToSomeType(source: Type, target: UnionOrIntersectionType): boolean {
6260-
for (const t of target.types) {
6261-
if (isTypeIdenticalTo(source, t)) {
6268+
function typeIdenticalToSomeType(type: Type, types: Type[]): boolean {
6269+
for (const t of types) {
6270+
if (isTypeIdenticalTo(t, type)) {
62626271
return true;
62636272
}
62646273
}
62656274
return false;
62666275
}
62676276

62686277
/**
6269-
* Return the reduced form of the source type. This type is computed by by removing all source
6270-
* constituents that have an identical match in the target type.
6278+
* Return a new union or intersection type computed by removing a given set of types
6279+
* from a given union or intersection type.
62716280
*/
6272-
function reduceUnionOrIntersectionType(source: UnionOrIntersectionType, target: UnionOrIntersectionType) {
6273-
let sourceTypes = source.types;
6274-
let sourceIndex = 0;
6275-
let modified = false;
6276-
while (sourceIndex < sourceTypes.length) {
6277-
if (typeIdenticalToSomeType(sourceTypes[sourceIndex], target)) {
6278-
if (!modified) {
6279-
sourceTypes = sourceTypes.slice(0);
6280-
modified = true;
6281-
}
6282-
sourceTypes.splice(sourceIndex, 1);
6283-
}
6284-
else {
6285-
sourceIndex++;
6281+
function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) {
6282+
const reducedTypes: Type[] = [];
6283+
for (const t of type.types) {
6284+
if (!typeIdenticalToSomeType(t, typesToRemove)) {
6285+
reducedTypes.push(t);
62866286
}
62876287
}
6288-
if (modified) {
6289-
return source.flags & TypeFlags.Union ? getUnionType(sourceTypes, /*noSubtypeReduction*/ true) : getIntersectionType(sourceTypes);
6290-
}
6291-
return source;
6288+
return type.flags & TypeFlags.Union ? getUnionType(reducedTypes, /*noSubtypeReduction*/ true) : getIntersectionType(reducedTypes);
62926289
}
62936290

62946291
function getInferenceCandidates(context: InferenceContext, index: number): Type[] {

0 commit comments

Comments
 (0)