Skip to content

Commit 203fd9f

Browse files
committed
Combine multiple separate code paths
1 parent 9b2d9cd commit 203fd9f

File tree

1 file changed

+86
-107
lines changed

1 file changed

+86
-107
lines changed

src/compiler/checker.ts

Lines changed: 86 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -15577,7 +15577,7 @@ namespace ts {
1557715577
// Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine
1557815578
const simplified = getSimplifiedType(target, /*writing*/ false);
1557915579
if (simplified !== target) {
15580-
inferFromTypesOnce(source, simplified);
15580+
invokeOnce(source, simplified, inferFromTypes);
1558115581
}
1558215582
else if (target.flags & TypeFlags.IndexedAccess) {
1558315583
const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false);
@@ -15586,7 +15586,7 @@ namespace ts {
1558615586
if (indexType.flags & TypeFlags.Instantiable) {
1558715587
const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false);
1558815588
if (simplified && simplified !== target) {
15589-
inferFromTypesOnce(source, simplified);
15589+
invokeOnce(source, simplified, inferFromTypes);
1559015590
}
1559115591
}
1559215592
}
@@ -15621,15 +15621,12 @@ namespace ts {
1562115621
inferFromTypes(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target));
1562215622
inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target));
1562315623
}
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-
}
1563015624
else if (target.flags & TypeFlags.Conditional && !contravariant) {
1563115625
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);
1563315630
}
1563415631
else if (source.flags & TypeFlags.Union) {
1563515632
// Source is a union or intersection type, infer from each constituent type
@@ -15658,50 +15655,22 @@ namespace ts {
1565815655
source = apparentSource;
1565915656
}
1566015657
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);
1568915659
}
1569015660
}
15661+
}
1569115662

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;
1570415669
}
15670+
(visited || (visited = createMap<number>())).set(key, 0);
15671+
const startCount = inferenceCount;
15672+
action(source, target);
15673+
visited.set(key, inferenceCount - startCount);
1570515674
}
1570615675

1570715676
function inferFromTypeArguments(sourceTypes: readonly Type[], targetTypes: readonly Type[], variances: readonly VarianceFlags[]) {
@@ -15738,76 +15707,64 @@ namespace ts {
1573815707
return undefined;
1573915708
}
1574015709

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) {
1574515711
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+
}
1574915730
}
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+
}
1575215744
}
15745+
inferenceBlocked = inferenceBlocked || saveInferenceBlocked;
1575315746
}
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.
1576115751
for (const t of targets) {
1576215752
if (getInferenceInfoForType(t)) {
15763-
inferFromTypes(source, t);
15753+
typeVariableCount++;
1576415754
}
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);
1580315757
}
1580415758
}
1580515759
}
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) {
1580815765
const savePriority = priority;
1580915766
priority |= InferencePriority.NakedTypeVariable;
15810-
for (const t of target.types) {
15767+
for (const t of targets) {
1581115768
if (getInferenceInfoForType(t)) {
1581215769
inferFromTypes(source, t);
1581315770
}
@@ -15873,6 +15830,28 @@ namespace ts {
1587315830
}
1587415831

1587515832
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) {
1587615855
if (isGenericMappedType(source) && isGenericMappedType(target)) {
1587715856
// The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer
1587815857
// from S to T and from X to Y.

0 commit comments

Comments
 (0)