@@ -334,6 +334,7 @@ namespace ts {
334334 const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
335335
336336 const globals = createSymbolTable();
337+ const deferredInferenceCache = createMap<Type | undefined>();
337338 let ambientModulesCache: Symbol[] | undefined;
338339 /**
339340 * List of every ambient module with a "*" wildcard.
@@ -4867,6 +4868,9 @@ namespace ts {
48674868 if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
48684869 return getTypeOfInstantiatedSymbol(symbol);
48694870 }
4871+ if (getCheckFlags(symbol) & CheckFlags.DeferredInferred) {
4872+ return inferTargetType((symbol as DeferredTransientSymbol).propertyType, (symbol as DeferredTransientSymbol).mappedType);
4873+ }
48704874 if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
48714875 return getTypeOfVariableOrParameterOrProperty(symbol);
48724876 }
@@ -11224,42 +11228,49 @@ namespace ts {
1122411228 * property is computed by inferring from the source property type to X for the type
1122511229 * variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for).
1122611230 */
11227- function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, mappedTypeStack: string[]): Type {
11228- const properties = getPropertiesOfType(source);
11229- let indexInfo = getIndexInfoOfType(source, IndexKind.String);
11230- if (properties.length === 0 && !indexInfo) {
11231- return undefined;
11232- }
11233- const typeParameter = <TypeParameter>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
11234- const inference = createInferenceInfo(typeParameter);
11235- const inferences = [inference];
11236- const templateType = getTemplateTypeFromMappedType(target);
11237- const readonlyMask = target.declaration.readonlyToken ? false : true;
11238- const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
11239- const members = createSymbolTable();
11240- for (const prop of properties) {
11241- const propType = getTypeOfSymbol(prop);
11242- // If any property contains context sensitive functions that have been skipped, the source type
11243- // is incomplete and we can't infer a meaningful input type.
11244- if (propType.flags & TypeFlags.ContainsAnyFunctionType) {
11231+ function inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type {
11232+ const key = source.id + "," + target.id;
11233+ if (deferredInferenceCache.has(key)) {
11234+ return deferredInferenceCache.get(key);
11235+ }
11236+ deferredInferenceCache.set(key, function() {
11237+ const properties = getPropertiesOfType(source);
11238+ let indexInfo = getIndexInfoOfType(source, IndexKind.String);
11239+ if (properties.length === 0 && !indexInfo) {
1124511240 return undefined;
1124611241 }
11247- const checkFlags = readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0;
11248- const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags);
11249- inferredProp.declarations = prop.declarations;
11250- inferredProp.type = inferTargetType(propType);
11251- members.set(prop.escapedName, inferredProp);
11252- }
11253- if (indexInfo) {
11254- indexInfo = createIndexInfo(inferTargetType(indexInfo.type), readonlyMask && indexInfo.isReadonly);
11255- }
11256- return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
11242+ const readonlyMask = target.declaration.readonlyToken ? false : true;
11243+ const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
11244+ const members = createSymbolTable();
11245+ for (const prop of properties) {
11246+ const propType = getTypeOfSymbol(prop);
11247+ // If any property contains context sensitive functions that have been skipped, the source type
11248+ // is incomplete and we can't infer a meaningful input type.
11249+ if (propType.flags & TypeFlags.ContainsAnyFunctionType) {
11250+ return undefined;
11251+ }
11252+ const checkFlags = CheckFlags.DeferredInferred | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0);
11253+ const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as DeferredTransientSymbol;
11254+ inferredProp.declarations = prop.declarations;
11255+ inferredProp.propertyType = propType; // not sure I need this.
11256+ inferredProp.mappedType = target;
11257+ members.set(prop.escapedName, inferredProp);
11258+ }
11259+ if (indexInfo) {
11260+ // TODO: Defer this too. BARREL OF LAUGHS RIGHT THERE
11261+ indexInfo = createIndexInfo(inferTargetType(indexInfo.type, target), readonlyMask && indexInfo.isReadonly);
11262+ }
11263+ return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
11264+ }());
11265+ }
1125711266
11258- function inferTargetType(sourceType: Type): Type {
11259- inference.candidates = undefined;
11260- inferTypes(inferences, sourceType, templateType, 0, mappedTypeStack);
11261- return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType;
11262- }
11267+ function inferTargetType(sourceType: Type, target: MappedType): Type {
11268+ const typeParameter = <TypeParameter>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
11269+ const templateType = getTemplateTypeFromMappedType(target);
11270+ const inference = createInferenceInfo(typeParameter);
11271+ inference.candidates = undefined;
11272+ inferTypes([inference], sourceType, templateType);
11273+ return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType;
1126311274 }
1126411275
1126511276 function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean) {
@@ -11275,7 +11286,7 @@ namespace ts {
1127511286 return undefined;
1127611287 }
1127711288
11278- function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, mappedTypeStack?: string[] ) {
11289+ function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
1127911290 let symbolStack: Symbol[];
1128011291 let visited: Map<boolean>;
1128111292 inferFromTypes(originalSource, originalTarget);
@@ -11492,13 +11503,7 @@ namespace ts {
1149211503 // such that direct inferences to T get priority over inferences to Partial<T>, for example.
1149311504 const inference = getInferenceInfoForType((<IndexType>constraintType).type);
1149411505 if (inference && !inference.isFixed) {
11495- const key = (source.symbol ? getSymbolId(source.symbol) + "," : "") + getSymbolId(target.symbol);
11496- if (contains(mappedTypeStack, key)) {
11497- return;
11498- }
11499- (mappedTypeStack || (mappedTypeStack = [])).push(key);
11500- const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target, mappedTypeStack);
11501- mappedTypeStack.pop();
11506+ const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
1150211507 if (inferredType) {
1150311508 const savePriority = priority;
1150411509 priority |= InferencePriority.MappedType;
0 commit comments