Merge pull request #34607 from microsoft/fix33490

Fix type inference regression
This commit is contained in:
Anders Hejlsberg
2019-10-21 12:50:38 -07:00
committed by GitHub
5 changed files with 275 additions and 22 deletions

View File

@@ -17354,10 +17354,7 @@ namespace ts {
// inferring a type parameter constraint. Instead, make a lower priority inference from
// the full source to whatever remains in the target. For example, when inferring from
// string to 'string | T', make a lower priority inference of string for T.
const savePriority = priority;
priority |= InferencePriority.NakedTypeVariable;
inferFromTypes(source, target);
priority = savePriority;
inferWithPriority(source, target, InferencePriority.NakedTypeVariable);
return;
}
source = getUnionType(sources);
@@ -17459,10 +17456,7 @@ namespace ts {
else if ((isLiteralType(source) || source.flags & TypeFlags.String) && target.flags & TypeFlags.Index) {
const empty = createEmptyObjectTypeFromStringLiteral(source);
contravariant = !contravariant;
const savePriority = priority;
priority |= InferencePriority.LiteralKeyof;
inferFromTypes(empty, (target as IndexType).type);
priority = savePriority;
inferWithPriority(empty, (target as IndexType).type, InferencePriority.LiteralKeyof);
contravariant = !contravariant;
}
else if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) {
@@ -17514,6 +17508,13 @@ namespace ts {
}
}
function inferWithPriority(source: Type, target: Type, newPriority: InferencePriority) {
const savePriority = priority;
priority |= newPriority;
inferFromTypes(source, target);
priority = savePriority;
}
function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) {
const key = source.id + "," + target.id;
const status = visited && visited.get(key);
@@ -17581,6 +17582,18 @@ namespace ts {
return undefined;
}
function getSingleTypeVariableFromIntersectionTypes(types: Type[]) {
let typeVariable: Type | undefined;
for (const type of types) {
const t = type.flags & TypeFlags.Intersection && find((<IntersectionType>type).types, t => !!getInferenceInfoForType(t));
if (!t || typeVariable && t !== typeVariable) {
return undefined;
}
typeVariable = t;
}
return typeVariable;
}
function inferToMultipleTypes(source: Type, targets: Type[], targetFlags: TypeFlags) {
let typeVariableCount = 0;
if (targetFlags & TypeFlags.Union) {
@@ -17608,6 +17621,16 @@ namespace ts {
}
}
}
if (typeVariableCount === 0) {
// If every target is an intersection of types containing a single naked type variable,
// make a lower priority inference to that type variable. This handles inferring from
// 'A | B' to 'T & (X | Y)' where we want to infer 'A | B' for T.
const intersectionTypeVariable = getSingleTypeVariableFromIntersectionTypes(targets);
if (intersectionTypeVariable) {
inferWithPriority(source, intersectionTypeVariable, InferencePriority.NakedTypeVariable);
}
return;
}
// If the target has a single naked type variable and no inference circularities were
// encountered above (meaning we explored the types fully), create a union of the source
// types from which no inferences have been made so far and infer from that union to the
@@ -17638,14 +17661,11 @@ namespace ts {
// we want to infer string for T, not Promise<string> | string. For intersection types
// we only infer to single naked type variables.
if (targetFlags & TypeFlags.Intersection ? typeVariableCount === 1 : typeVariableCount > 0) {
const savePriority = priority;
priority |= InferencePriority.NakedTypeVariable;
for (const t of targets) {
if (getInferenceInfoForType(t)) {
inferFromTypes(source, t);
inferWithPriority(source, t, InferencePriority.NakedTypeVariable);
}
}
priority = savePriority;
}
}
@@ -17666,14 +17686,13 @@ namespace ts {
if (inference && !inference.isFixed) {
const inferredType = inferTypeForHomomorphicMappedType(source, target, <IndexType>constraintType);
if (inferredType) {
const savePriority = priority;
// We assign a lower priority to inferences made from types containing non-inferrable
// types because we may only have a partial result (i.e. we may have failed to make
// reverse inferences for some properties).
priority |= getObjectFlags(source) & ObjectFlags.NonInferrableType ?
InferencePriority.PartialHomomorphicMappedType : InferencePriority.HomomorphicMappedType;
inferFromTypes(inferredType, inference.typeParameter);
priority = savePriority;
inferWithPriority(inferredType, inference.typeParameter,
getObjectFlags(source) & ObjectFlags.NonInferrableType ?
InferencePriority.PartialHomomorphicMappedType :
InferencePriority.HomomorphicMappedType);
}
}
return true;
@@ -17681,10 +17700,7 @@ namespace ts {
if (constraintType.flags & TypeFlags.TypeParameter) {
// We're inferring from some source type S to a mapped type { [P in K]: X }, where K is a type
// parameter. First infer from 'keyof S' to K.
const savePriority = priority;
priority |= InferencePriority.MappedTypeConstraint;
inferFromTypes(getIndexType(source), constraintType);
priority = savePriority;
inferWithPriority(getIndexType(source), constraintType, InferencePriority.MappedTypeConstraint);
// If K is constrained to a type C, also infer to C. Thus, for a mapped type { [P in K]: X },
// where K extends keyof T, we make the same inferences as for a homomorphic mapped type
// { [P in keyof T]: X }. This enables us to make meaningful inferences when the target is a