@@ -6148,14 +6148,25 @@ namespace ts {
6148
6148
function inferFromTypes(source: Type, target: Type) {
6149
6149
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
6150
6150
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
6151
- // Source and target are both unions or both intersections. To improve the quality of
6152
- // inferences we first reduce the types by removing constituents that are identically
6153
- // matched by a constituent in the other type. For example, when inferring from
6154
- // 'string | string[]' to 'string | T', we reduce the types to 'string[]' and 'T'.
6155
- const reducedSource = reduceUnionOrIntersectionType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target);
6156
- const reducedTarget = reduceUnionOrIntersectionType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source);
6157
- source = reducedSource;
6158
- target = reducedTarget;
6151
+ // Source and target are both unions or both intersections. First, find each
6152
+ // target constituent type that has an identically matching source constituent
6153
+ // type, and for each such target constituent type infer from the type to itself.
6154
+ // When inferring from a type to itself we effectively find all type parameter
6155
+ // occurrences within that type and infer themselves as their type arguments.
6156
+ let matchingTypes: Type[];
6157
+ for (const t of (<UnionOrIntersectionType>target).types) {
6158
+ if (typeIdenticalToSomeType(t, (<UnionOrIntersectionType>source).types)) {
6159
+ (matchingTypes || (matchingTypes = [])).push(t);
6160
+ inferFromTypes(t, t);
6161
+ }
6162
+ }
6163
+ // Next, to improve the quality of inferences, reduce the source and target types by
6164
+ // removing the identically matched constituents. For example, when inferring from
6165
+ // 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'.
6166
+ if (matchingTypes) {
6167
+ source = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>source, matchingTypes);
6168
+ target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
6169
+ }
6159
6170
}
6160
6171
if (target.flags & TypeFlags.TypeParameter) {
6161
6172
// If target is a type parameter, make an inference, unless the source type contains
@@ -6317,39 +6328,27 @@ namespace ts {
6317
6328
}
6318
6329
}
6319
6330
6320
- function typeIdenticalToSomeType(source : Type, target: UnionOrIntersectionType ): boolean {
6321
- for (const t of target. types) {
6322
- if (isTypeIdenticalTo(source, t )) {
6331
+ function typeIdenticalToSomeType(type : Type, types: Type[] ): boolean {
6332
+ for (const t of types) {
6333
+ if (isTypeIdenticalTo(t, type )) {
6323
6334
return true;
6324
6335
}
6325
6336
}
6326
6337
return false;
6327
6338
}
6328
6339
6329
6340
/**
6330
- * Return the reduced form of the source type. This type is computed by by removing all source
6331
- * constituents that have an identical match in the target type.
6341
+ * Return a new union or intersection type computed by removing a given set of types
6342
+ * from a given union or intersection type.
6332
6343
*/
6333
- function reduceUnionOrIntersectionType(source: UnionOrIntersectionType, target: UnionOrIntersectionType) {
6334
- let sourceTypes = source.types;
6335
- let sourceIndex = 0;
6336
- let modified = false;
6337
- while (sourceIndex < sourceTypes.length) {
6338
- if (typeIdenticalToSomeType(sourceTypes[sourceIndex], target)) {
6339
- if (!modified) {
6340
- sourceTypes = sourceTypes.slice(0);
6341
- modified = true;
6342
- }
6343
- sourceTypes.splice(sourceIndex, 1);
6344
- }
6345
- else {
6346
- sourceIndex++;
6344
+ function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) {
6345
+ const reducedTypes: Type[] = [];
6346
+ for (const t of type.types) {
6347
+ if (!typeIdenticalToSomeType(t, typesToRemove)) {
6348
+ reducedTypes.push(t);
6347
6349
}
6348
6350
}
6349
- if (modified) {
6350
- return source.flags & TypeFlags.Union ? getUnionType(sourceTypes, /*noSubtypeReduction*/ true) : getIntersectionType(sourceTypes);
6351
- }
6352
- return source;
6351
+ return type.flags & TypeFlags.Union ? getUnionType(reducedTypes, /*noSubtypeReduction*/ true) : getIntersectionType(reducedTypes);
6353
6352
}
6354
6353
6355
6354
function getInferenceCandidates(context: InferenceContext, index: number): Type[] {
0 commit comments