@@ -15577,7 +15577,7 @@ namespace ts {
15577
15577
// Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine
15578
15578
const simplified = getSimplifiedType(target, /*writing*/ false);
15579
15579
if (simplified !== target) {
15580
- inferFromTypesOnce (source, simplified);
15580
+ invokeOnce (source, simplified, inferFromTypes );
15581
15581
}
15582
15582
else if (target.flags & TypeFlags.IndexedAccess) {
15583
15583
const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false);
@@ -15586,7 +15586,7 @@ namespace ts {
15586
15586
if (indexType.flags & TypeFlags.Instantiable) {
15587
15587
const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false);
15588
15588
if (simplified && simplified !== target) {
15589
- inferFromTypesOnce (source, simplified);
15589
+ invokeOnce (source, simplified, inferFromTypes );
15590
15590
}
15591
15591
}
15592
15592
}
@@ -15621,15 +15621,12 @@ namespace ts {
15621
15621
inferFromTypes(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target));
15622
15622
inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target));
15623
15623
}
15624
- else if (target.flags & TypeFlags.Union) {
15625
- inferToUnionType(source, <UnionType>target);
15626
- }
15627
- else if (target.flags & TypeFlags.Intersection) {
15628
- inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, /*isIntersection*/ true);
15629
- }
15630
15624
else if (target.flags & TypeFlags.Conditional && !contravariant) {
15631
15625
const targetTypes = [getTrueTypeFromConditionalType(<ConditionalType>target), getFalseTypeFromConditionalType(<ConditionalType>target)];
15632
- inferToMultipleTypes(source, targetTypes, /*isIntersection*/ false);
15626
+ inferToMultipleTypes(source, targetTypes, target.flags);
15627
+ }
15628
+ else if (target.flags & TypeFlags.UnionOrIntersection) {
15629
+ inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, target.flags);
15633
15630
}
15634
15631
else if (source.flags & TypeFlags.Union) {
15635
15632
// Source is a union or intersection type, infer from each constituent type
@@ -15658,50 +15655,22 @@ namespace ts {
15658
15655
source = apparentSource;
15659
15656
}
15660
15657
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
15661
- const key = source.id + "," + target.id;
15662
- const visitCount = visited && visited.get(key);
15663
- if (visitCount !== undefined) {
15664
- inferenceCount += visitCount;
15665
- return;
15666
- }
15667
- (visited || (visited = createMap<number>())).set(key, 0);
15668
- // If we are already processing another target type with the same associated symbol (such as
15669
- // an instantiation of the same generic type), we do not explore this target as it would yield
15670
- // no further inferences. We exclude the static side of classes from this check since it shares
15671
- // its symbol with the instance side which would lead to false positives.
15672
- const startCount = inferenceCount;
15673
- const isNonConstructorObject = target.flags & TypeFlags.Object &&
15674
- !(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class);
15675
- const symbol = isNonConstructorObject ? target.symbol : undefined;
15676
- if (symbol) {
15677
- if (contains(symbolStack, symbol)) {
15678
- inferenceBlocked = true;
15679
- return;
15680
- }
15681
- (symbolStack || (symbolStack = [])).push(symbol);
15682
- inferFromObjectTypes(source, target);
15683
- symbolStack.pop();
15684
- }
15685
- else {
15686
- inferFromObjectTypes(source, target);
15687
- }
15688
- visited.set(key, inferenceCount - startCount);
15658
+ invokeOnce(source, target, inferFromObjectTypes);
15689
15659
}
15690
15660
}
15661
+ }
15691
15662
15692
- function inferFromTypesOnce(source: Type, target: Type) {
15693
- const key = source.id + "," + target.id;
15694
- const count = visited && visited.get(key);
15695
- if (count !== undefined) {
15696
- inferenceCount += count;
15697
- }
15698
- else {
15699
- (visited || (visited = createMap<number>())).set(key, 0);
15700
- const startCount = inferenceCount;
15701
- inferFromTypes(source, target);
15702
- visited.set(key, inferenceCount - startCount);
15703
- }
15663
+ function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) {
15664
+ const key = source.id + "," + target.id;
15665
+ const count = visited && visited.get(key);
15666
+ if (count !== undefined) {
15667
+ inferenceCount += count;
15668
+ return;
15704
15669
}
15670
+ (visited || (visited = createMap<number>())).set(key, 0);
15671
+ const startCount = inferenceCount;
15672
+ action(source, target);
15673
+ visited.set(key, inferenceCount - startCount);
15705
15674
}
15706
15675
15707
15676
function inferFromTypeArguments(sourceTypes: readonly Type[], targetTypes: readonly Type[], variances: readonly VarianceFlags[]) {
@@ -15738,76 +15707,64 @@ namespace ts {
15738
15707
return undefined;
15739
15708
}
15740
15709
15741
- function inferToMultipleTypes(source: Type, targets: Type[], isIntersection: boolean) {
15742
- // We infer from types that are not naked type variables first so that inferences we
15743
- // make from nested naked type variables and given slightly higher priority by virtue
15744
- // of being first in the candidates array.
15710
+ function inferToMultipleTypes(source: Type, targets: Type[], targetFlags: TypeFlags) {
15745
15711
let typeVariableCount = 0;
15746
- for (const t of targets) {
15747
- if (getInferenceInfoForType(t)) {
15748
- typeVariableCount++;
15712
+ if (targetFlags & TypeFlags.Union) {
15713
+ const sources = source.flags & TypeFlags.Union ? (<UnionType>source).types : [source];
15714
+ const matched = new Array<boolean>(sources.length);
15715
+ const saveInferenceBlocked = inferenceBlocked;
15716
+ inferenceBlocked = false;
15717
+ // First infer to types that are not naked type variables. For each source type we
15718
+ // track whether inferences were made from that particular type to some target.
15719
+ for (const t of targets) {
15720
+ if (getInferenceInfoForType(t)) {
15721
+ typeVariableCount++;
15722
+ }
15723
+ else {
15724
+ for (let i = 0; i < sources.length; i++) {
15725
+ const count = inferenceCount;
15726
+ inferFromTypes(sources[i], t);
15727
+ if (count !== inferenceCount) matched[i] = true;
15728
+ }
15729
+ }
15749
15730
}
15750
- else {
15751
- inferFromTypes(source, t);
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
+ // have been made so far and infer from that union to the naked type variable.
15734
+ if (typeVariableCount === 1 && !inferenceBlocked) {
15735
+ const unmatched = flatMap(sources, (s, i) => matched[i] ? undefined : s);
15736
+ if (unmatched.length) {
15737
+ const s = getUnionType(unmatched);
15738
+ for (const t of targets) {
15739
+ if (getInferenceInfoForType(t)) {
15740
+ inferFromTypes(s, t);
15741
+ }
15742
+ }
15743
+ }
15752
15744
}
15745
+ inferenceBlocked = inferenceBlocked || saveInferenceBlocked;
15753
15746
}
15754
- // Inferences directly to naked type variables are given lower priority as they are
15755
- // less specific. For example, when inferring from Promise<string> to T | Promise<T>,
15756
- // we want to infer string for T, not Promise<string> | string. For intersection types
15757
- // we only infer to single naked type variables.
15758
- if (isIntersection ? typeVariableCount === 1 : typeVariableCount !== 0) {
15759
- const savePriority = priority;
15760
- priority |= InferencePriority.NakedTypeVariable;
15747
+ else {
15748
+ // We infer from types that are not naked type variables first so that inferences we
15749
+ // make from nested naked type variables and given slightly higher priority by virtue
15750
+ // of being first in the candidates array.
15761
15751
for (const t of targets) {
15762
15752
if (getInferenceInfoForType(t)) {
15763
- inferFromTypes(source, t) ;
15753
+ typeVariableCount++ ;
15764
15754
}
15765
- }
15766
- priority = savePriority;
15767
- }
15768
- }
15769
-
15770
- function inferToUnionType(source: Type, target: UnionType) {
15771
- const sources = source.flags & TypeFlags.Union ? (<UnionType>source).types : [source];
15772
- const matched = new Array<boolean>(sources.length);
15773
- let typeVariableCount = 0;
15774
- const saveInferenceBlocked = inferenceBlocked;
15775
- inferenceBlocked = false;
15776
- // First infer to types that are not naked type variables. For each source type we
15777
- // track whether inferences were made from that particular type to some target.
15778
- for (const t of target.types) {
15779
- if (getInferenceInfoForType(t)) {
15780
- typeVariableCount++;
15781
- }
15782
- else {
15783
- for (let i = 0; i < sources.length; i++) {
15784
- const count = inferenceCount;
15785
- inferFromTypes(sources[i], t);
15786
- if (count !== inferenceCount) matched[i] = true;
15787
- }
15788
- }
15789
- }
15790
- // If there are naked type variables in the target, create a union of the source types
15791
- // from which no inferences have been made so far and infer from that union to each naked
15792
- // type variable. If there is more than one naked type variable, or if inference was blocked
15793
- // (meaning we didn't explore the types fully), give lower priority to the inferences as
15794
- // they are less specific.
15795
- if (typeVariableCount === 1 && !inferenceBlocked) {
15796
- const unmatched = flatMap(sources, (s, i) => matched[i] ? undefined : s);
15797
- if (unmatched.length) {
15798
- const s = getUnionType(unmatched);
15799
- for (const t of target.types) {
15800
- if (getInferenceInfoForType(t)) {
15801
- inferFromTypes(s, t);
15802
- }
15755
+ else {
15756
+ inferFromTypes(source, t);
15803
15757
}
15804
15758
}
15805
15759
}
15806
- inferenceBlocked = inferenceBlocked || saveInferenceBlocked;
15807
- if (typeVariableCount > 0) {
15760
+ // Inferences directly to naked type variables are given lower priority as they are
15761
+ // less specific. For example, when inferring from Promise<string> to T | Promise<T>,
15762
+ // we want to infer string for T, not Promise<string> | string. For intersection types
15763
+ // we only infer to single naked type variables.
15764
+ if (targetFlags & TypeFlags.Intersection ? typeVariableCount === 1 : typeVariableCount > 0) {
15808
15765
const savePriority = priority;
15809
15766
priority |= InferencePriority.NakedTypeVariable;
15810
- for (const t of target.types ) {
15767
+ for (const t of targets ) {
15811
15768
if (getInferenceInfoForType(t)) {
15812
15769
inferFromTypes(source, t);
15813
15770
}
@@ -15873,6 +15830,28 @@ namespace ts {
15873
15830
}
15874
15831
15875
15832
function inferFromObjectTypes(source: Type, target: Type) {
15833
+ // If we are already processing another target type with the same associated symbol (such as
15834
+ // an instantiation of the same generic type), we do not explore this target as it would yield
15835
+ // no further inferences. We exclude the static side of classes from this check since it shares
15836
+ // its symbol with the instance side which would lead to false positives.
15837
+ const isNonConstructorObject = target.flags & TypeFlags.Object &&
15838
+ !(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class);
15839
+ const symbol = isNonConstructorObject ? target.symbol : undefined;
15840
+ if (symbol) {
15841
+ if (contains(symbolStack, symbol)) {
15842
+ inferenceBlocked = true;
15843
+ return;
15844
+ }
15845
+ (symbolStack || (symbolStack = [])).push(symbol);
15846
+ inferFromObjectTypesWorker(source, target);
15847
+ symbolStack.pop();
15848
+ }
15849
+ else {
15850
+ inferFromObjectTypesWorker(source, target);
15851
+ }
15852
+ }
15853
+
15854
+ function inferFromObjectTypesWorker(source: Type, target: Type) {
15876
15855
if (isGenericMappedType(source) && isGenericMappedType(target)) {
15877
15856
// The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer
15878
15857
// from S to T and from X to Y.
0 commit comments