Cut off inference for recursive mapped types

Previously, when inferring to a self-referential (or otherwise recursive)
homomorphic mapped type from a source type that also has recursive
references, type inference would enter infinite recursion.

Now there is a more complex stack for mapped type inference. It mirrors
the existing symbolStack but (1) includes the source type and (2) is
passed through inferTypeForHomomorphicMappedType, which is actually
called outside of inferTypes, and so restarts the symbolStack cache
every time.
This commit is contained in:
Nathan Shively-Sanders 2017-11-30 09:33:05 -08:00
parent 5ee640d2b6
commit b1316e589e

View file

@ -11127,7 +11127,7 @@ namespace ts {
* property is computed by inferring from the source property type to X for the type
* variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for).
*/
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type {
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, mappedTypeStack: [Type, Symbol][]): Type {
const properties = getPropertiesOfType(source);
let indexInfo = getIndexInfoOfType(source, IndexKind.String);
if (properties.length === 0 && !indexInfo) {
@ -11160,7 +11160,7 @@ namespace ts {
function inferTargetType(sourceType: Type): Type {
inference.candidates = undefined;
inferTypes(inferences, sourceType, templateType);
inferTypes(inferences, sourceType, templateType, 0, mappedTypeStack);
return inference.candidates ? getUnionType(inference.candidates, /*subtypeReduction*/ true) : emptyObjectType;
}
}
@ -11178,7 +11178,7 @@ namespace ts {
return undefined;
}
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, mappedTypeStack?: [Type, Symbol][]) {
let symbolStack: Symbol[];
let visited: Map<boolean>;
inferFromTypes(originalSource, originalTarget);
@ -11395,7 +11395,12 @@ namespace ts {
// such that direct inferences to T get priority over inferences to Partial<T>, for example.
const inference = getInferenceInfoForType((<IndexType>constraintType).type);
if (inference && !inference.isFixed) {
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
if (contains(mappedTypeStack, [source, target.symbol], ([s1,t1],[s2,t2]) => s1 === s2 && t1 === t2)) {
return;
}
(mappedTypeStack || (mappedTypeStack = [])).push([source, target.symbol]);
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target, mappedTypeStack);
mappedTypeStack.pop();
if (inferredType) {
const savePriority = priority;
priority |= InferencePriority.MappedType;