@@ -15497,37 +15497,27 @@ namespace ts {
15497
15497
}
15498
15498
return;
15499
15499
}
15500
- // Find each source constituent type that has an identically matching target constituent
15501
- // type, and for each such type infer from the type to itself. When inferring from a
15502
- // type to itself we effectively find all type parameter occurrences within that type
15503
- // and infer themselves as their type arguments. We have special handling for numeric
15504
- // and string literals because the number and string types are not represented as unions
15505
- // of all their possible values.
15506
- let matchingTypes: Type[] | undefined;
15507
- for (const t of (<UnionOrIntersectionType>source).types) {
15508
- const matched = findMatchedType(t, <UnionOrIntersectionType>target);
15509
- if (matched) {
15510
- (matchingTypes || (matchingTypes = [])).push(matched);
15511
- inferFromTypes(matched, matched);
15512
- }
15513
- }
15514
- // Next, to improve the quality of inferences, reduce the source and target types by
15515
- // removing the identically matched constituents. For example, when inferring from
15516
- // 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'.
15517
- if (matchingTypes) {
15518
- const s = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>source, matchingTypes);
15519
- const t = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
15520
- if (!(s && t)) return;
15521
- source = s;
15522
- target = t;
15500
+ // First, infer between exactly matching source and target constituents and remove
15501
+ // the matching types. Types exactly match when they are identical or, in union
15502
+ // types, when the source is a literal and the target is the corresponding primitive.
15503
+ const matching = target.flags & TypeFlags.Union ? isTypeOrBaseExactlyMatchedBy : isTypeExactlyMatchedBy;
15504
+ const [tempSources, tempTargets] = inferFromMatchingTypes((<UnionOrIntersectionType>source).types, (<UnionOrIntersectionType>target).types, matching);
15505
+ // Next, infer between closely matching source and target constituents and remove
15506
+ // the matching types. Types closely match when they are instantiations of the same
15507
+ // object type or instantiations of the same type alias.
15508
+ const [sources, targets] = inferFromMatchingTypes(tempSources, tempTargets, isTypeCloselyMatchedBy);
15509
+ if (sources.length === 0 || targets.length === 0) {
15510
+ return;
15523
15511
}
15512
+ source = source.flags & TypeFlags.Union ? getUnionType(sources) : getIntersectionType(sources);
15513
+ target = target.flags & TypeFlags.Union ? getUnionType(targets) : getIntersectionType(targets);
15524
15514
}
15525
15515
else if (target.flags & TypeFlags.Union && !(target.flags & TypeFlags.EnumLiteral) || target.flags & TypeFlags.Intersection) {
15526
- const matched = findMatchedType(source, <UnionOrIntersectionType>target);
15527
- if (matched) {
15528
- inferFromTypes(matched, matched) ;
15529
- return;
15530
- }
15516
+ // This block of code is an optimized version of the block above for the simpler case
15517
+ // of a singleton source type.
15518
+ const matching = target.flags & TypeFlags.Union ? isTypeOrBaseExactlyMatchedBy : isTypeExactlyMatchedBy ;
15519
+ if (inferFromMatchingType(source, (<UnionOrIntersectionType>target).types, matching)) return;
15520
+ if (inferFromMatchingType(source, (<UnionOrIntersectionType>target).types, isTypeCloselyMatchedBy)) return;
15531
15521
}
15532
15522
else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
15533
15523
target = getActualTypeVariable(target);
@@ -15675,6 +15665,35 @@ namespace ts {
15675
15665
visited.set(key, inferenceCount - startCount);
15676
15666
}
15677
15667
15668
+ function inferFromMatchingType(source: Type, targets: Type[], matches: (s: Type, t: Type) => boolean) {
15669
+ let matched = false;
15670
+ for (const t of targets) {
15671
+ if (matches(source, t)) {
15672
+ inferFromTypes(source, t);
15673
+ matched = true;
15674
+ }
15675
+ }
15676
+ return matched;
15677
+ }
15678
+
15679
+ function inferFromMatchingTypes(sources: Type[], targets: Type[], matches: (s: Type, t: Type) => boolean): [Type[], Type[]] {
15680
+ let matchedSources: Type[] | undefined;
15681
+ let matchedTargets: Type[] | undefined;
15682
+ for (const t of targets) {
15683
+ for (const s of sources) {
15684
+ if (matches(s, t)) {
15685
+ inferFromTypes(s, t);
15686
+ matchedSources = appendIfUnique(matchedSources, s);
15687
+ matchedTargets = appendIfUnique(matchedTargets, t);
15688
+ }
15689
+ }
15690
+ }
15691
+ return [
15692
+ matchedSources ? filter(sources, t => !contains(matchedSources, t)) : sources,
15693
+ matchedTargets ? filter(targets, t => !contains(matchedTargets, t)) : targets,
15694
+ ];
15695
+ }
15696
+
15678
15697
function inferFromTypeArguments(sourceTypes: readonly Type[], targetTypes: readonly Type[], variances: readonly VarianceFlags[]) {
15679
15698
const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length;
15680
15699
for (let i = 0; i < count; i++) {
@@ -15955,47 +15974,24 @@ namespace ts {
15955
15974
}
15956
15975
}
15957
15976
15958
- function isMatchableType (type: Type) {
15977
+ function isNonObjectOrAnonymousType (type: Type) {
15959
15978
// We exclude non-anonymous object types because some frameworks (e.g. Ember) rely on the ability to
15960
15979
// infer between types that don't witness their type variables. Such types would otherwise be eliminated
15961
15980
// because they appear identical.
15962
15981
return !(type.flags & TypeFlags.Object) || !!(getObjectFlags(type) & ObjectFlags.Anonymous);
15963
15982
}
15964
15983
15965
- function typeMatchedBySomeType(type: Type, types: Type[]): boolean {
15966
- for (const t of types) {
15967
- if (t === type || isMatchableType(t) && isMatchableType(type) && isTypeIdenticalTo(t, type)) {
15968
- return true;
15969
- }
15970
- }
15971
- return false;
15984
+ function isTypeExactlyMatchedBy(s: Type, t: Type) {
15985
+ return s === t || isNonObjectOrAnonymousType(s) && isNonObjectOrAnonymousType(t) && isTypeIdenticalTo(s, t);
15972
15986
}
15973
15987
15974
- function findMatchedType(type: Type, target: UnionOrIntersectionType) {
15975
- if (typeMatchedBySomeType(type, target.types)) {
15976
- return type;
15977
- }
15978
- if (type.flags & (TypeFlags.NumberLiteral | TypeFlags.StringLiteral) && target.flags & TypeFlags.Union) {
15979
- const base = getBaseTypeOfLiteralType(type);
15980
- if (typeMatchedBySomeType(base, target.types)) {
15981
- return base;
15982
- }
15983
- }
15984
- return undefined;
15988
+ function isTypeOrBaseExactlyMatchedBy(s: Type, t: Type) {
15989
+ return isTypeExactlyMatchedBy(s, t) || !!(s.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) && isTypeIdenticalTo(getBaseTypeOfLiteralType(s), t);
15985
15990
}
15986
15991
15987
- /**
15988
- * Return a new union or intersection type computed by removing a given set of types
15989
- * from a given union or intersection type.
15990
- */
15991
- function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) {
15992
- const reducedTypes: Type[] = [];
15993
- for (const t of type.types) {
15994
- if (!typeMatchedBySomeType(t, typesToRemove)) {
15995
- reducedTypes.push(t);
15996
- }
15997
- }
15998
- return reducedTypes.length ? type.flags & TypeFlags.Union ? getUnionType(reducedTypes) : getIntersectionType(reducedTypes) : undefined;
15992
+ function isTypeCloselyMatchedBy(s: Type, t: Type) {
15993
+ return !!(s.flags & TypeFlags.Object && t.flags & TypeFlags.Object && s.symbol && s.symbol === t.symbol ||
15994
+ s.aliasSymbol && s.aliasTypeArguments && s.aliasSymbol === t.aliasSymbol);
15999
15995
}
16000
15996
16001
15997
function hasPrimitiveConstraint(type: TypeParameter): boolean {
0 commit comments