@@ -15463,7 +15463,7 @@ namespace ts {
15463
15463
let bivariant = false;
15464
15464
let propagationType: Type;
15465
15465
let inferenceCount = 0;
15466
- let inferenceBlocked = false;
15466
+ let inferenceIncomplete = false;
15467
15467
let allowComplexConstraintInference = true;
15468
15468
inferFromTypes(originalSource, originalTarget);
15469
15469
@@ -15710,14 +15710,16 @@ namespace ts {
15710
15710
function inferToMultipleTypes(source: Type, targets: Type[], targetFlags: TypeFlags) {
15711
15711
let typeVariableCount = 0;
15712
15712
if (targetFlags & TypeFlags.Union) {
15713
+ let nakedTypeVariable: Type | undefined;
15713
15714
const sources = source.flags & TypeFlags.Union ? (<UnionType>source).types : [source];
15714
15715
const matched = new Array<boolean>(sources.length);
15715
- const saveInferenceBlocked = inferenceBlocked ;
15716
- inferenceBlocked = false;
15716
+ const saveInferenceIncomplete = inferenceIncomplete ;
15717
+ inferenceIncomplete = false;
15717
15718
// First infer to types that are not naked type variables. For each source type we
15718
15719
// track whether inferences were made from that particular type to some target.
15719
15720
for (const t of targets) {
15720
15721
if (getInferenceInfoForType(t)) {
15722
+ nakedTypeVariable = t;
15721
15723
typeVariableCount++;
15722
15724
}
15723
15725
else {
@@ -15728,21 +15730,18 @@ namespace ts {
15728
15730
}
15729
15731
}
15730
15732
}
15731
- // If the target has a single naked type variable and inference wasn't blocked (meaning
15732
- // we explored the types fully), create a union of the source types from which no inferences
15733
+ const inferenceComplete = !inferenceIncomplete;
15734
+ inferenceIncomplete = inferenceIncomplete || saveInferenceIncomplete;
15735
+ // If the target has a single naked type variable and inference completed (meaning we
15736
+ // explored the types fully), create a union of the source types from which no inferences
15733
15737
// have been made so far and infer from that union to the naked type variable.
15734
- if (typeVariableCount === 1 && !inferenceBlocked ) {
15738
+ if (typeVariableCount === 1 && inferenceComplete ) {
15735
15739
const unmatched = flatMap(sources, (s, i) => matched[i] ? undefined : s);
15736
15740
if (unmatched.length) {
15737
- const s = getUnionType(unmatched);
15738
- for (const t of targets) {
15739
- if (getInferenceInfoForType(t)) {
15740
- inferFromTypes(s, t);
15741
- }
15742
- }
15741
+ inferFromTypes(getUnionType(unmatched), nakedTypeVariable!);
15742
+ return;
15743
15743
}
15744
15744
}
15745
- inferenceBlocked = inferenceBlocked || saveInferenceBlocked;
15746
15745
}
15747
15746
else {
15748
15747
// We infer from types that are not naked type variables first so that inferences we
@@ -15839,7 +15838,7 @@ namespace ts {
15839
15838
const symbol = isNonConstructorObject ? target.symbol : undefined;
15840
15839
if (symbol) {
15841
15840
if (contains(symbolStack, symbol)) {
15842
- inferenceBlocked = true;
15841
+ inferenceIncomplete = true;
15843
15842
return;
15844
15843
}
15845
15844
(symbolStack || (symbolStack = [])).push(symbol);
@@ -15955,10 +15954,13 @@ namespace ts {
15955
15954
}
15956
15955
15957
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.
15958
15960
return !(type.flags & TypeFlags.Object) || !!(getObjectFlags(type) & ObjectFlags.Anonymous);
15959
15961
}
15960
15962
15961
- function typeIdenticalToSomeType (type: Type, types: Type[]): boolean {
15963
+ function typeMatchedBySomeType (type: Type, types: Type[]): boolean {
15962
15964
for (const t of types) {
15963
15965
if (t === type || isMatchableType(t) && isMatchableType(type) && isTypeIdenticalTo(t, type)) {
15964
15966
return true;
@@ -15968,12 +15970,12 @@ namespace ts {
15968
15970
}
15969
15971
15970
15972
function findMatchedType(type: Type, target: UnionOrIntersectionType) {
15971
- if (typeIdenticalToSomeType (type, target.types)) {
15973
+ if (typeMatchedBySomeType (type, target.types)) {
15972
15974
return type;
15973
15975
}
15974
15976
if (type.flags & (TypeFlags.NumberLiteral | TypeFlags.StringLiteral) && target.flags & TypeFlags.Union) {
15975
15977
const base = getBaseTypeOfLiteralType(type);
15976
- if (typeIdenticalToSomeType (base, target.types)) {
15978
+ if (typeMatchedBySomeType (base, target.types)) {
15977
15979
return base;
15978
15980
}
15979
15981
}
@@ -15987,7 +15989,7 @@ namespace ts {
15987
15989
function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) {
15988
15990
const reducedTypes: Type[] = [];
15989
15991
for (const t of type.types) {
15990
- if (!typeIdenticalToSomeType (t, typesToRemove)) {
15992
+ if (!typeMatchedBySomeType (t, typesToRemove)) {
15991
15993
reducedTypes.push(t);
15992
15994
}
15993
15995
}
0 commit comments