@@ -339,6 +339,7 @@ namespace ts {
339339 const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
340340
341341 const globals = createSymbolTable();
342+ const reverseMappedCache = createMap<Type | undefined>();
342343 let ambientModulesCache: Symbol[] | undefined;
343344 /**
344345 * List of every ambient module with a "*" wildcard.
@@ -2860,7 +2861,10 @@ namespace ts {
28602861 typeElements.push(<ConstructSignatureDeclaration>signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature, context));
28612862 }
28622863 if (resolvedType.stringIndexInfo) {
2863- typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String, context));
2864+ const indexInfo = resolvedType.objectFlags & ObjectFlags.ReverseMapped ?
2865+ createIndexInfo(anyType, resolvedType.stringIndexInfo.isReadonly, resolvedType.stringIndexInfo.declaration) :
2866+ resolvedType.stringIndexInfo;
2867+ typeElements.push(indexInfoToIndexSignatureDeclarationHelper(indexInfo, IndexKind.String, context));
28642868 }
28652869 if (resolvedType.numberIndexInfo) {
28662870 typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number, context));
@@ -2872,7 +2876,7 @@ namespace ts {
28722876 }
28732877
28742878 for (const propertySymbol of properties) {
2875- const propertyType = getTypeOfSymbol(propertySymbol);
2879+ const propertyType = getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped ? anyType : getTypeOfSymbol(propertySymbol);
28762880 const saveEnclosingDeclaration = context.enclosingDeclaration;
28772881 context.enclosingDeclaration = undefined;
28782882 const propertyName = symbolToName(propertySymbol, context, SymbolFlags.Value, /*expectsIdentifier*/ true);
@@ -3681,7 +3685,10 @@ namespace ts {
36813685 writePunctuation(writer, SyntaxKind.SemicolonToken);
36823686 writer.writeLine();
36833687 }
3684- buildIndexSignatureDisplay(resolved.stringIndexInfo, writer, IndexKind.String, enclosingDeclaration, globalFlags, symbolStack);
3688+ const stringIndexInfo = resolved.objectFlags & ObjectFlags.ReverseMapped && resolved.stringIndexInfo ?
3689+ createIndexInfo(anyType, resolved.stringIndexInfo.isReadonly, resolved.stringIndexInfo.declaration) :
3690+ resolved.stringIndexInfo;
3691+ buildIndexSignatureDisplay(stringIndexInfo, writer, IndexKind.String, enclosingDeclaration, globalFlags, symbolStack);
36853692 buildIndexSignatureDisplay(resolved.numberIndexInfo, writer, IndexKind.Number, enclosingDeclaration, globalFlags, symbolStack);
36863693 for (const p of resolved.properties) {
36873694 if (globalFlags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral) {
@@ -3692,7 +3699,7 @@ namespace ts {
36923699 writer.reportPrivateInBaseOfClassExpression(symbolName(p));
36933700 }
36943701 }
3695- const t = getTypeOfSymbol(p);
3702+ const t = getCheckFlags(p) & CheckFlags.ReverseMapped ? anyType : getTypeOfSymbol(p);
36963703 if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) {
36973704 const signatures = getSignaturesOfType(t, SignatureKind.Call);
36983705 for (const signature of signatures) {
@@ -4900,6 +4907,9 @@ namespace ts {
49004907 if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
49014908 return getTypeOfInstantiatedSymbol(symbol);
49024909 }
4910+ if (getCheckFlags(symbol) & CheckFlags.ReverseMapped) {
4911+ return getTypeOfReverseMappedSymbol(symbol as ReverseMappedSymbol);
4912+ }
49034913 if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
49044914 return getTypeOfVariableOrParameterOrProperty(symbol);
49054915 }
@@ -6110,6 +6120,23 @@ namespace ts {
61106120 }
61116121 }
61126122
6123+ function resolveReverseMappedTypeMembers(type: ReverseMappedType) {
6124+ const indexInfo = getIndexInfoOfType(type.source, IndexKind.String);
6125+ const readonlyMask = type.mappedType.declaration.readonlyToken ? false : true;
6126+ const optionalMask = type.mappedType.declaration.questionToken ? 0 : SymbolFlags.Optional;
6127+ const stringIndexInfo = indexInfo && createIndexInfo(inferReverseMappedType(indexInfo.type, type.mappedType), readonlyMask && indexInfo.isReadonly);
6128+ const members = createSymbolTable();
6129+ for (const prop of getPropertiesOfType(type.source)) {
6130+ const checkFlags = CheckFlags.ReverseMapped | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0);
6131+ const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as ReverseMappedSymbol;
6132+ inferredProp.declarations = prop.declarations;
6133+ inferredProp.propertyType = getTypeOfSymbol(prop);
6134+ inferredProp.mappedType = type.mappedType;
6135+ members.set(prop.escapedName, inferredProp);
6136+ }
6137+ setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
6138+ }
6139+
61136140 /** Resolve the members of a mapped type { [P in K]: T } */
61146141 function resolveMappedTypeMembers(type: MappedType) {
61156142 const members: SymbolTable = createSymbolTable();
@@ -6249,6 +6276,9 @@ namespace ts {
62496276 else if ((<ObjectType>type).objectFlags & ObjectFlags.ClassOrInterface) {
62506277 resolveClassOrInterfaceMembers(<InterfaceType>type);
62516278 }
6279+ else if ((<ReverseMappedType>type).objectFlags & ObjectFlags.ReverseMapped) {
6280+ resolveReverseMappedTypeMembers(type as ReverseMappedType);
6281+ }
62526282 else if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
62536283 resolveAnonymousTypeMembers(<AnonymousType>type);
62546284 }
@@ -11275,42 +11305,45 @@ namespace ts {
1127511305 * property is computed by inferring from the source property type to X for the type
1127611306 * variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for).
1127711307 */
11278- function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, mappedTypeStack: string[]): Type {
11308+ function inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type {
11309+ const key = source.id + "," + target.id;
11310+ if (reverseMappedCache.has(key)) {
11311+ return reverseMappedCache.get(key);
11312+ }
11313+ reverseMappedCache.set(key, undefined);
11314+ const type = createReverseMappedType(source, target);
11315+ reverseMappedCache.set(key, type);
11316+ return type;
11317+ }
11318+
11319+ function createReverseMappedType(source: Type, target: MappedType) {
1127911320 const properties = getPropertiesOfType(source);
11280- let indexInfo = getIndexInfoOfType(source, IndexKind.String);
11281- if (properties.length === 0 && !indexInfo) {
11321+ if (properties.length === 0 && !getIndexInfoOfType(source, IndexKind.String)) {
1128211322 return undefined;
1128311323 }
11284- const typeParameter = <TypeParameter>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
11285- const inference = createInferenceInfo(typeParameter);
11286- const inferences = [inference];
11287- const templateType = getTemplateTypeFromMappedType(target);
11288- const readonlyMask = target.declaration.readonlyToken ? false : true;
11289- const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
11290- const members = createSymbolTable();
11324+ // If any property contains context sensitive functions that have been skipped, the source type
11325+ // is incomplete and we can't infer a meaningful input type.
1129111326 for (const prop of properties) {
11292- const propType = getTypeOfSymbol(prop);
11293- // If any property contains context sensitive functions that have been skipped, the source type
11294- // is incomplete and we can't infer a meaningful input type.
11295- if (propType.flags & TypeFlags.ContainsAnyFunctionType) {
11327+ if (getTypeOfSymbol(prop).flags & TypeFlags.ContainsAnyFunctionType) {
1129611328 return undefined;
1129711329 }
11298- const checkFlags = readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0;
11299- const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags);
11300- inferredProp.declarations = prop.declarations;
11301- inferredProp.type = inferTargetType(propType);
11302- members.set(prop.escapedName, inferredProp);
1130311330 }
11304- if (indexInfo) {
11305- indexInfo = createIndexInfo(inferTargetType(indexInfo.type), readonlyMask && indexInfo.isReadonly);
11306- }
11307- return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
11331+ const reversed = createObjectType(ObjectFlags.ReverseMapped | ObjectFlags.Anonymous, /*symbol*/ undefined) as ReverseMappedType;
11332+ reversed.source = source;
11333+ reversed.mappedType = target;
11334+ return reversed;
11335+ }
1130811336
11309- function inferTargetType(sourceType: Type): Type {
11310- inference.candidates = undefined;
11311- inferTypes(inferences, sourceType, templateType, 0, mappedTypeStack);
11312- return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType;
11313- }
11337+ function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol) {
11338+ return inferReverseMappedType(symbol.propertyType, symbol.mappedType);
11339+ }
11340+
11341+ function inferReverseMappedType(sourceType: Type, target: MappedType): Type {
11342+ const typeParameter = <TypeParameter>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
11343+ const templateType = getTemplateTypeFromMappedType(target);
11344+ const inference = createInferenceInfo(typeParameter);
11345+ inferTypes([inference], sourceType, templateType);
11346+ return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType;
1131411347 }
1131511348
1131611349 function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean) {
@@ -11326,7 +11359,7 @@ namespace ts {
1132611359 return undefined;
1132711360 }
1132811361
11329- function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, mappedTypeStack?: string[] ) {
11362+ function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
1133011363 let symbolStack: Symbol[];
1133111364 let visited: Map<boolean>;
1133211365 inferFromTypes(originalSource, originalTarget);
@@ -11543,13 +11576,7 @@ namespace ts {
1154311576 // such that direct inferences to T get priority over inferences to Partial<T>, for example.
1154411577 const inference = getInferenceInfoForType((<IndexType>constraintType).type);
1154511578 if (inference && !inference.isFixed) {
11546- const key = (source.symbol ? getSymbolId(source.symbol) + "," : "") + getSymbolId(target.symbol);
11547- if (contains(mappedTypeStack, key)) {
11548- return;
11549- }
11550- (mappedTypeStack || (mappedTypeStack = [])).push(key);
11551- const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target, mappedTypeStack);
11552- mappedTypeStack.pop();
11579+ const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
1155311580 if (inferredType) {
1155411581 const savePriority = priority;
1155511582 priority |= InferencePriority.MappedType;
0 commit comments