@@ -2159,7 +2159,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
21592159 /** Key is "/path/to/a.ts|/path/to/b.ts". */
21602160 var amalgamatedDuplicates: Map<string, DuplicateInfoForFiles> | undefined;
21612161 var reverseMappedCache = new Map<string, Type | undefined>();
2162- var homomorphicMappedTypeInferenceStack: string[] = [];
21632162 var ambientModulesCache: Symbol[] | undefined;
21642163 /**
21652164 * List of every ambient module with a "*" wildcard.
@@ -2277,6 +2276,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
22772276 var potentialReflectCollisions: Node[] = [];
22782277 var potentialUnusedRenamedBindingElementsInTypes: BindingElement[] = [];
22792278 var awaitedTypeStack: number[] = [];
2279+ var reverseMappedSourceStack: Type[] = [];
2280+ var reverseMappedTargetStack: Type[] = [];
2281+ var reverseExpandingFlags = ExpandingFlags.None;
22802282
22812283 var diagnostics = createDiagnosticCollection();
22822284 var suggestionDiagnostics = createDiagnosticCollection();
@@ -13583,7 +13585,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1358313585 const modifiers = getMappedTypeModifiers(type.mappedType);
1358413586 const readonlyMask = modifiers & MappedTypeModifiers.IncludeReadonly ? false : true;
1358513587 const optionalMask = modifiers & MappedTypeModifiers.IncludeOptional ? 0 : SymbolFlags.Optional;
13586- const indexInfos = indexInfo ? [createIndexInfo(stringType, inferReverseMappedType(indexInfo.type, type.mappedType, type.constraintType), readonlyMask && indexInfo.isReadonly)] : emptyArray;
13588+ const indexInfos = indexInfo ? [createIndexInfo(stringType, inferReverseMappedType(indexInfo.type, type.mappedType, type.constraintType) || unknownType , readonlyMask && indexInfo.isReadonly)] : emptyArray;
1358713589 const members = createSymbolTable();
1358813590 const limitedConstraint = getLimitedConstraint(type);
1358913591 for (const prop of getPropertiesOfType(type.source)) {
@@ -25102,13 +25104,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2510225104 if (reverseMappedCache.has(cacheKey)) {
2510325105 return reverseMappedCache.get(cacheKey);
2510425106 }
25105- const recursionKey = source.id + "," + (target.target || target).id;
25106- if (contains(homomorphicMappedTypeInferenceStack, recursionKey)) {
25107- return undefined;
25108- }
25109- homomorphicMappedTypeInferenceStack.push(recursionKey);
2511025107 const type = createReverseMappedType(source, target, constraint);
25111- homomorphicMappedTypeInferenceStack.pop();
2511225108 reverseMappedCache.set(cacheKey, type);
2511325109 return type;
2511425110 }
@@ -25132,10 +25128,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2513225128 // For arrays and tuples we infer new arrays and tuples where the reverse mapping has been
2513325129 // applied to the element type(s).
2513425130 if (isArrayType(source)) {
25135- return createArrayType(inferReverseMappedType(getTypeArguments(source)[0], target, constraint), isReadonlyArrayType(source));
25131+ const elementType = inferReverseMappedType(getTypeArguments(source)[0], target, constraint);
25132+ if (!elementType) {
25133+ return undefined;
25134+ }
25135+ return createArrayType(elementType, isReadonlyArrayType(source));
2513625136 }
2513725137 if (isTupleType(source)) {
2513825138 const elementTypes = map(getElementTypes(source), t => inferReverseMappedType(t, target, constraint));
25139+ if (!every(elementTypes, (t): t is Type => !!t)) {
25140+ return undefined;
25141+ }
2513925142 const elementFlags = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ?
2514025143 sameMap(source.target.elementFlags, f => f & ElementFlags.Optional ? ElementFlags.Required : f) :
2514125144 source.target.elementFlags;
@@ -25150,22 +25153,43 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2515025153 return reversed;
2515125154 }
2515225155
25153- function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol) {
25156+ function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol): Type {
2515425157 const links = getSymbolLinks(symbol);
2515525158 if (!links.type) {
25156- links.type = inferReverseMappedType(symbol.links.propertyType, symbol.links.mappedType, symbol.links.constraintType);
25159+ links.type = inferReverseMappedType(symbol.links.propertyType, symbol.links.mappedType, symbol.links.constraintType) || unknownType ;
2515725160 }
2515825161 return links.type;
2515925162 }
2516025163
25161- function inferReverseMappedType (sourceType: Type, target: MappedType, constraint: IndexType): Type {
25164+ function inferReverseMappedTypeWorker (sourceType: Type, target: MappedType, constraint: IndexType): Type {
2516225165 const typeParameter = getIndexedAccessType(constraint.type, getTypeParameterFromMappedType(target)) as TypeParameter;
2516325166 const templateType = getTemplateTypeFromMappedType(target);
2516425167 const inference = createInferenceInfo(typeParameter);
2516525168 inferTypes([inference], sourceType, templateType);
2516625169 return getTypeFromInference(inference) || unknownType;
2516725170 }
2516825171
25172+ function inferReverseMappedType(source: Type, target: MappedType, constraint: IndexType): Type | undefined {
25173+ const cacheKey = source.id + "," + target.id + "," + constraint.id;
25174+ if (reverseMappedCache.has(cacheKey)) {
25175+ return reverseMappedCache.get(cacheKey) || unknownType;
25176+ }
25177+ reverseMappedSourceStack.push(source);
25178+ reverseMappedTargetStack.push(target);
25179+ const saveExpandingFlags = reverseExpandingFlags;
25180+ if (isDeeplyNestedType(source, reverseMappedSourceStack, reverseMappedSourceStack.length, 2)) reverseExpandingFlags |= ExpandingFlags.Source;
25181+ if (isDeeplyNestedType(target, reverseMappedTargetStack, reverseMappedTargetStack.length, 2)) reverseExpandingFlags |= ExpandingFlags.Target;
25182+ let type;
25183+ if (reverseExpandingFlags !== ExpandingFlags.Both) {
25184+ type = inferReverseMappedTypeWorker(source, target, constraint);
25185+ }
25186+ reverseMappedSourceStack.pop();
25187+ reverseMappedTargetStack.pop();
25188+ reverseExpandingFlags = saveExpandingFlags;
25189+ reverseMappedCache.set(cacheKey, type);
25190+ return type;
25191+ }
25192+
2516925193 function* getUnmatchedProperties(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean): IterableIterator<Symbol> {
2517025194 const properties = getPropertiesOfType(target);
2517125195 for (const targetProp of properties) {
0 commit comments