Skip to content

Commit b1316e5

Browse files
committed
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.
1 parent 5ee640d commit b1316e5

1 file changed

Lines changed: 9 additions & 4 deletions

File tree

src/compiler/checker.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11127,7 +11127,7 @@ namespace ts {
1112711127
* property is computed by inferring from the source property type to X for the type
1112811128
* variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for).
1112911129
*/
11130-
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type {
11130+
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, mappedTypeStack: [Type, Symbol][]): Type {
1113111131
const properties = getPropertiesOfType(source);
1113211132
let indexInfo = getIndexInfoOfType(source, IndexKind.String);
1113311133
if (properties.length === 0 && !indexInfo) {
@@ -11160,7 +11160,7 @@ namespace ts {
1116011160

1116111161
function inferTargetType(sourceType: Type): Type {
1116211162
inference.candidates = undefined;
11163-
inferTypes(inferences, sourceType, templateType);
11163+
inferTypes(inferences, sourceType, templateType, 0, mappedTypeStack);
1116411164
return inference.candidates ? getUnionType(inference.candidates, /*subtypeReduction*/ true) : emptyObjectType;
1116511165
}
1116611166
}
@@ -11178,7 +11178,7 @@ namespace ts {
1117811178
return undefined;
1117911179
}
1118011180

11181-
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
11181+
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, mappedTypeStack?: [Type, Symbol][]) {
1118211182
let symbolStack: Symbol[];
1118311183
let visited: Map<boolean>;
1118411184
inferFromTypes(originalSource, originalTarget);
@@ -11395,7 +11395,12 @@ namespace ts {
1139511395
// such that direct inferences to T get priority over inferences to Partial<T>, for example.
1139611396
const inference = getInferenceInfoForType((<IndexType>constraintType).type);
1139711397
if (inference && !inference.isFixed) {
11398-
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
11398+
if (contains(mappedTypeStack, [source, target.symbol], ([s1,t1],[s2,t2]) => s1 === s2 && t1 === t2)) {
11399+
return;
11400+
}
11401+
(mappedTypeStack || (mappedTypeStack = [])).push([source, target.symbol]);
11402+
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target, mappedTypeStack);
11403+
mappedTypeStack.pop();
1139911404
if (inferredType) {
1140011405
const savePriority = priority;
1140111406
priority |= InferencePriority.MappedType;

0 commit comments

Comments
 (0)