@@ -15308,7 +15308,7 @@ namespace ts {
1530815308 objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeVariables) ||
1530915309 objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ||
1531015310 objectFlags & ObjectFlags.Mapped ||
15311- type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeVariables(<UnionOrIntersectionType>type));
15311+ type.flags & TypeFlags.UnionOrIntersection && !(type.flags & TypeFlags.EnumLiteral) && couldUnionOrIntersectionContainTypeVariables(<UnionOrIntersectionType>type));
1531215312 }
1531315313
1531415314 function couldUnionOrIntersectionContainTypeVariables(type: UnionOrIntersectionType): boolean {
@@ -15460,7 +15460,7 @@ namespace ts {
1546015460 let visited: Map<number>;
1546115461 let bivariant = false;
1546215462 let propagationType: Type;
15463- let inferenceCount = 0 ;
15463+ let inferenceMatch = false ;
1546415464 let inferenceIncomplete = false;
1546515465 let allowComplexConstraintInference = true;
1546615466 inferFromTypes(originalSource, originalTarget);
@@ -15485,46 +15485,50 @@ namespace ts {
1548515485 inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol));
1548615486 return;
1548715487 }
15488- if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union && !(source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.EnumLiteral) ||
15489- source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
15490- // Source and target are both unions or both intersections. If source and target
15491- // are the same type, just relate each constituent type to itself.
15492- if (source === target) {
15493- for (const t of (<UnionOrIntersectionType>source).types) {
15494- inferFromTypes(t, t);
15488+ if (source === target && source.flags & TypeFlags.UnionOrIntersection) {
15489+ // When source and target are the same union or intersection type, just relate each constituent
15490+ // type to itself.
15491+ for (const t of (<UnionOrIntersectionType>source).types) {
15492+ inferFromTypes(t, t);
15493+ }
15494+ return;
15495+ }
15496+ if (target.flags & TypeFlags.Union) {
15497+ if (source.flags & TypeFlags.Union) {
15498+ // First, infer between identically matching source and target constituents and remove the
15499+ // matching types.
15500+ const [tempSources, tempTargets] = inferFromMatchingTypes((<UnionType>source).types, (<UnionType>target).types, isTypeOrBaseIdenticalTo);
15501+ // Next, infer between closely matching source and target constituents and remove
15502+ // the matching types. Types closely match when they are instantiations of the same
15503+ // object type or instantiations of the same type alias.
15504+ const [sources, targets] = inferFromMatchingTypes(tempSources, tempTargets, isTypeCloselyMatchedBy);
15505+ if (sources.length === 0 || targets.length === 0) {
15506+ return;
1549515507 }
15496- return;
15508+ source = getUnionType(sources);
15509+ target = getUnionType(targets);
1549715510 }
15498- // Find each source constituent type that has an identically matching target constituent
15499- // type, and for each such type infer from the type to itself. When inferring from a
15500- // type to itself we effectively find all type parameter occurrences within that type
15501- // and infer themselves as their type arguments. We have special handling for numeric
15502- // and string literals because the number and string types are not represented as unions
15503- // of all their possible values.
15504- let matchingTypes: Type[] | undefined;
15505- for (const t of (<UnionOrIntersectionType>source).types) {
15506- const matched = findMatchedType(t, <UnionOrIntersectionType>target);
15507- if (matched) {
15508- (matchingTypes || (matchingTypes = [])).push(matched);
15509- inferFromTypes(matched, matched);
15510- }
15511- }
15512- // Next, to improve the quality of inferences, reduce the source and target types by
15513- // removing the identically matched constituents. For example, when inferring from
15514- // 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'.
15515- if (matchingTypes) {
15516- const s = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>source, matchingTypes);
15517- const t = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
15518- if (!(s && t)) return;
15519- source = s;
15520- target = t;
15521- }
15522- }
15523- else if (target.flags & TypeFlags.Union && !(target.flags & TypeFlags.EnumLiteral) || target.flags & TypeFlags.Intersection) {
15524- const matched = findMatchedType(source, <UnionOrIntersectionType>target);
15525- if (matched) {
15526- inferFromTypes(matched, matched);
15527- return;
15511+ else {
15512+ if (inferFromMatchingType(source, (<UnionType>target).types, isTypeOrBaseIdenticalTo)) return;
15513+ if (inferFromMatchingType(source, (<UnionType>target).types, isTypeCloselyMatchedBy)) return;
15514+ }
15515+ }
15516+ else if (target.flags & TypeFlags.Intersection && some((<IntersectionType>target).types, t => !!getInferenceInfoForType(t))) {
15517+ // We reduce intersection types only when they contain naked type parameters. For example, when
15518+ // inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and
15519+ // infer { extra: any } for T. But when inferring to 'string[] & Iterable<T>' we want to keep the
15520+ // string[] on the source side and infer string for T.
15521+ if (source.flags & TypeFlags.Intersection) {
15522+ // Infer between identically matching source and target constituents and remove the matching types.
15523+ const [sources, targets] = inferFromMatchingTypes((<IntersectionType>source).types, (<IntersectionType>target).types, isTypeIdenticalTo);
15524+ if (sources.length === 0 || targets.length === 0) {
15525+ return;
15526+ }
15527+ source = getIntersectionType(sources);
15528+ target = getIntersectionType(targets);
15529+ }
15530+ else if (!(source.flags & TypeFlags.Union)) {
15531+ if (inferFromMatchingType(source, (<IntersectionType>target).types, isTypeIdenticalTo)) return;
1552815532 }
1552915533 }
1553015534 else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
@@ -15570,7 +15574,7 @@ namespace ts {
1557015574 clearCachedInferences(inferences);
1557115575 }
1557215576 }
15573- inferenceCount++ ;
15577+ inferenceMatch = true ;
1557415578 return;
1557515579 }
1557615580 else {
@@ -15662,15 +15666,50 @@ namespace ts {
1566215666
1566315667 function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) {
1566415668 const key = source.id + "," + target.id;
15665- const count = visited && visited.get(key);
15666- if (count !== undefined) {
15667- inferenceCount += count;
15669+ const status = visited && visited.get(key);
15670+ if (status !== undefined) {
15671+ if (status & 1) inferenceMatch = true;
15672+ if (status & 2) inferenceIncomplete = true;
1566815673 return;
1566915674 }
1567015675 (visited || (visited = createMap<number>())).set(key, 0);
15671- const startCount = inferenceCount;
15676+ const saveInferenceMatch = inferenceMatch;
15677+ const saveInferenceIncomplete = inferenceIncomplete;
15678+ inferenceMatch = false;
15679+ inferenceIncomplete = false;
1567215680 action(source, target);
15673- visited.set(key, inferenceCount - startCount);
15681+ visited.set(key, (inferenceMatch ? 1 : 0) | (inferenceIncomplete ? 2 : 0));
15682+ inferenceMatch = inferenceMatch || saveInferenceMatch;
15683+ inferenceIncomplete = inferenceIncomplete || saveInferenceIncomplete;
15684+ }
15685+
15686+ function inferFromMatchingType(source: Type, targets: Type[], matches: (s: Type, t: Type) => boolean) {
15687+ let matched = false;
15688+ for (const t of targets) {
15689+ if (matches(source, t)) {
15690+ inferFromTypes(source, t);
15691+ matched = true;
15692+ }
15693+ }
15694+ return matched;
15695+ }
15696+
15697+ function inferFromMatchingTypes(sources: Type[], targets: Type[], matches: (s: Type, t: Type) => boolean): [Type[], Type[]] {
15698+ let matchedSources: Type[] | undefined;
15699+ let matchedTargets: Type[] | undefined;
15700+ for (const t of targets) {
15701+ for (const s of sources) {
15702+ if (matches(s, t)) {
15703+ inferFromTypes(s, t);
15704+ matchedSources = appendIfUnique(matchedSources, s);
15705+ matchedTargets = appendIfUnique(matchedTargets, t);
15706+ }
15707+ }
15708+ }
15709+ return [
15710+ matchedSources ? filter(sources, t => !contains(matchedSources, t)) : sources,
15711+ matchedTargets ? filter(targets, t => !contains(matchedTargets, t)) : targets,
15712+ ];
1567415713 }
1567515714
1567615715 function inferFromTypeArguments(sourceTypes: readonly Type[], targetTypes: readonly Type[], variances: readonly VarianceFlags[]) {
@@ -15724,9 +15763,11 @@ namespace ts {
1572415763 }
1572515764 else {
1572615765 for (let i = 0; i < sources.length; i++) {
15727- const count = inferenceCount;
15766+ const saveInferenceMatch = inferenceMatch;
15767+ inferenceMatch = false;
1572815768 inferFromTypes(sources[i], t);
15729- if (count !== inferenceCount) matched[i] = true;
15769+ if (inferenceMatch) matched[i] = true;
15770+ inferenceMatch = inferenceMatch || saveInferenceMatch;
1573015771 }
1573115772 }
1573215773 }
@@ -15953,47 +15994,13 @@ namespace ts {
1595315994 }
1595415995 }
1595515996
15956- function isMatchableType(type: Type) {
15957- // We exclude non-anonymous object types because some frameworks (e.g. Ember) rely on the ability to
15958- // infer between types that don't witness their type variables. Such types would otherwise be eliminated
15959- // because they appear identical.
15960- return !(type.flags & TypeFlags.Object) || !!(getObjectFlags(type) & ObjectFlags.Anonymous);
15961- }
15962-
15963- function typeMatchedBySomeType(type: Type, types: Type[]): boolean {
15964- for (const t of types) {
15965- if (t === type || isMatchableType(t) && isMatchableType(type) && isTypeIdenticalTo(t, type)) {
15966- return true;
15967- }
15968- }
15969- return false;
15970- }
15971-
15972- function findMatchedType(type: Type, target: UnionOrIntersectionType) {
15973- if (typeMatchedBySomeType(type, target.types)) {
15974- return type;
15975- }
15976- if (type.flags & (TypeFlags.NumberLiteral | TypeFlags.StringLiteral) && target.flags & TypeFlags.Union) {
15977- const base = getBaseTypeOfLiteralType(type);
15978- if (typeMatchedBySomeType(base, target.types)) {
15979- return base;
15980- }
15981- }
15982- return undefined;
15997+ function isTypeOrBaseIdenticalTo(s: Type, t: Type) {
15998+ return isTypeIdenticalTo(s, t) || !!(s.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) && isTypeIdenticalTo(getBaseTypeOfLiteralType(s), t);
1598315999 }
1598416000
15985- /**
15986- * Return a new union or intersection type computed by removing a given set of types
15987- * from a given union or intersection type.
15988- */
15989- function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) {
15990- const reducedTypes: Type[] = [];
15991- for (const t of type.types) {
15992- if (!typeMatchedBySomeType(t, typesToRemove)) {
15993- reducedTypes.push(t);
15994- }
15995- }
15996- return reducedTypes.length ? type.flags & TypeFlags.Union ? getUnionType(reducedTypes) : getIntersectionType(reducedTypes) : undefined;
16001+ function isTypeCloselyMatchedBy(s: Type, t: Type) {
16002+ return !!(s.flags & TypeFlags.Object && t.flags & TypeFlags.Object && s.symbol && s.symbol === t.symbol ||
16003+ s.aliasSymbol && s.aliasTypeArguments && s.aliasSymbol === t.aliasSymbol);
1599716004 }
1599816005
1599916006 function hasPrimitiveConstraint(type: TypeParameter): boolean {
0 commit comments