Skip to content

Commit 8f15a2e

Browse files
committed
Fix type inference regression
1 parent e8782ae commit 8f15a2e

File tree

1 file changed

+37
-21
lines changed

1 file changed

+37
-21
lines changed

src/compiler/checker.ts

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17354,10 +17354,7 @@ namespace ts {
1735417354
// inferring a type parameter constraint. Instead, make a lower priority inference from
1735517355
// the full source to whatever remains in the target. For example, when inferring from
1735617356
// string to 'string | T', make a lower priority inference of string for T.
17357-
const savePriority = priority;
17358-
priority |= InferencePriority.NakedTypeVariable;
17359-
inferFromTypes(source, target);
17360-
priority = savePriority;
17357+
inferWithPriority(source, target, InferencePriority.NakedTypeVariable);
1736117358
return;
1736217359
}
1736317360
source = getUnionType(sources);
@@ -17459,10 +17456,7 @@ namespace ts {
1745917456
else if ((isLiteralType(source) || source.flags & TypeFlags.String) && target.flags & TypeFlags.Index) {
1746017457
const empty = createEmptyObjectTypeFromStringLiteral(source);
1746117458
contravariant = !contravariant;
17462-
const savePriority = priority;
17463-
priority |= InferencePriority.LiteralKeyof;
17464-
inferFromTypes(empty, (target as IndexType).type);
17465-
priority = savePriority;
17459+
inferWithPriority(empty, (target as IndexType).type, InferencePriority.LiteralKeyof);
1746617460
contravariant = !contravariant;
1746717461
}
1746817462
else if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) {
@@ -17514,6 +17508,13 @@ namespace ts {
1751417508
}
1751517509
}
1751617510

17511+
function inferWithPriority(source: Type, target: Type, newPriority: InferencePriority) {
17512+
const savePriority = priority;
17513+
priority |= newPriority;
17514+
inferFromTypes(source, target);
17515+
priority = savePriority;
17516+
}
17517+
1751717518
function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) {
1751817519
const key = source.id + "," + target.id;
1751917520
const status = visited && visited.get(key);
@@ -17581,6 +17582,18 @@ namespace ts {
1758117582
return undefined;
1758217583
}
1758317584

17585+
function getSingleTypeVariableFromIntersectionTypes(types: Type[]) {
17586+
let typeVariable: Type | undefined;
17587+
for (const type of types) {
17588+
const t = type.flags & TypeFlags.Intersection && find((<IntersectionType>type).types, t => !!getInferenceInfoForType(t));
17589+
if (!t || typeVariable && t !== typeVariable) {
17590+
return undefined;
17591+
}
17592+
typeVariable = t;
17593+
}
17594+
return typeVariable;
17595+
}
17596+
1758417597
function inferToMultipleTypes(source: Type, targets: Type[], targetFlags: TypeFlags) {
1758517598
let typeVariableCount = 0;
1758617599
if (targetFlags & TypeFlags.Union) {
@@ -17608,6 +17621,16 @@ namespace ts {
1760817621
}
1760917622
}
1761017623
}
17624+
if (typeVariableCount === 0) {
17625+
// If every target is an intersection of types containing a single naked type variable,
17626+
// make a lower priority inference to that type variable. This handles inferring from
17627+
// 'A | B' to 'T & (X | Y)' where we want to infer 'A | B' for T.
17628+
const intersectionTypeVariable = getSingleTypeVariableFromIntersectionTypes(targets);
17629+
if (intersectionTypeVariable) {
17630+
inferWithPriority(source, intersectionTypeVariable, InferencePriority.NakedTypeVariable);
17631+
}
17632+
return;
17633+
}
1761117634
// If the target has a single naked type variable and no inference circularities were
1761217635
// encountered above (meaning we explored the types fully), create a union of the source
1761317636
// types from which no inferences have been made so far and infer from that union to the
@@ -17638,14 +17661,11 @@ namespace ts {
1763817661
// we want to infer string for T, not Promise<string> | string. For intersection types
1763917662
// we only infer to single naked type variables.
1764017663
if (targetFlags & TypeFlags.Intersection ? typeVariableCount === 1 : typeVariableCount > 0) {
17641-
const savePriority = priority;
17642-
priority |= InferencePriority.NakedTypeVariable;
1764317664
for (const t of targets) {
1764417665
if (getInferenceInfoForType(t)) {
17645-
inferFromTypes(source, t);
17666+
inferWithPriority(source, t, InferencePriority.NakedTypeVariable);
1764617667
}
1764717668
}
17648-
priority = savePriority;
1764917669
}
1765017670
}
1765117671

@@ -17666,25 +17686,21 @@ namespace ts {
1766617686
if (inference && !inference.isFixed) {
1766717687
const inferredType = inferTypeForHomomorphicMappedType(source, target, <IndexType>constraintType);
1766817688
if (inferredType) {
17669-
const savePriority = priority;
1767017689
// We assign a lower priority to inferences made from types containing non-inferrable
1767117690
// types because we may only have a partial result (i.e. we may have failed to make
1767217691
// reverse inferences for some properties).
17673-
priority |= getObjectFlags(source) & ObjectFlags.NonInferrableType ?
17674-
InferencePriority.PartialHomomorphicMappedType : InferencePriority.HomomorphicMappedType;
17675-
inferFromTypes(inferredType, inference.typeParameter);
17676-
priority = savePriority;
17692+
inferWithPriority(inferredType, inference.typeParameter,
17693+
getObjectFlags(source) & ObjectFlags.NonInferrableType ?
17694+
InferencePriority.PartialHomomorphicMappedType :
17695+
InferencePriority.HomomorphicMappedType);
1767717696
}
1767817697
}
1767917698
return true;
1768017699
}
1768117700
if (constraintType.flags & TypeFlags.TypeParameter) {
1768217701
// We're inferring from some source type S to a mapped type { [P in K]: X }, where K is a type
1768317702
// parameter. First infer from 'keyof S' to K.
17684-
const savePriority = priority;
17685-
priority |= InferencePriority.MappedTypeConstraint;
17686-
inferFromTypes(getIndexType(source), constraintType);
17687-
priority = savePriority;
17703+
inferWithPriority(getIndexType(source), constraintType, InferencePriority.MappedTypeConstraint);
1768817704
// If K is constrained to a type C, also infer to C. Thus, for a mapped type { [P in K]: X },
1768917705
// where K extends keyof T, we make the same inferences as for a homomorphic mapped type
1769017706
// { [P in keyof T]: X }. This enables us to make meaningful inferences when the target is a

0 commit comments

Comments
 (0)