@@ -15308,7 +15308,7 @@ namespace ts {
15308
15308
objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeVariables) ||
15309
15309
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ||
15310
15310
objectFlags & ObjectFlags.Mapped ||
15311
- type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeVariables(<UnionOrIntersectionType>type));
15311
+ type.flags & TypeFlags.UnionOrIntersection && !(type.flags & TypeFlags.EnumLiteral) && couldUnionOrIntersectionContainTypeVariables(<UnionOrIntersectionType>type));
15312
15312
}
15313
15313
15314
15314
function couldUnionOrIntersectionContainTypeVariables(type: UnionOrIntersectionType): boolean {
@@ -15460,7 +15460,7 @@ namespace ts {
15460
15460
let visited: Map<number>;
15461
15461
let bivariant = false;
15462
15462
let propagationType: Type;
15463
- let inferenceCount = 0 ;
15463
+ let inferenceMatch = false ;
15464
15464
let inferenceIncomplete = false;
15465
15465
let allowComplexConstraintInference = true;
15466
15466
inferFromTypes(originalSource, originalTarget);
@@ -15485,46 +15485,50 @@ namespace ts {
15485
15485
inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol));
15486
15486
return;
15487
15487
}
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;
15495
15507
}
15496
- return;
15508
+ source = getUnionType(sources);
15509
+ target = getUnionType(targets);
15497
15510
}
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;
15528
15532
}
15529
15533
}
15530
15534
else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
@@ -15570,7 +15574,7 @@ namespace ts {
15570
15574
clearCachedInferences(inferences);
15571
15575
}
15572
15576
}
15573
- inferenceCount++ ;
15577
+ inferenceMatch = true ;
15574
15578
return;
15575
15579
}
15576
15580
else {
@@ -15662,15 +15666,50 @@ namespace ts {
15662
15666
15663
15667
function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) {
15664
15668
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;
15668
15673
return;
15669
15674
}
15670
15675
(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;
15672
15680
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
+ ];
15674
15713
}
15675
15714
15676
15715
function inferFromTypeArguments(sourceTypes: readonly Type[], targetTypes: readonly Type[], variances: readonly VarianceFlags[]) {
@@ -15724,9 +15763,11 @@ namespace ts {
15724
15763
}
15725
15764
else {
15726
15765
for (let i = 0; i < sources.length; i++) {
15727
- const count = inferenceCount;
15766
+ const saveInferenceMatch = inferenceMatch;
15767
+ inferenceMatch = false;
15728
15768
inferFromTypes(sources[i], t);
15729
- if (count !== inferenceCount) matched[i] = true;
15769
+ if (inferenceMatch) matched[i] = true;
15770
+ inferenceMatch = inferenceMatch || saveInferenceMatch;
15730
15771
}
15731
15772
}
15732
15773
}
@@ -15953,47 +15994,13 @@ namespace ts {
15953
15994
}
15954
15995
}
15955
15996
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);
15983
15999
}
15984
16000
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);
15997
16004
}
15998
16005
15999
16006
function hasPrimitiveConstraint(type: TypeParameter): boolean {
0 commit comments