@@ -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