@@ -5142,9 +5142,26 @@ namespace ts {
51425142 return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword);
51435143 }
51445144
5145+ function shouldUsePlaceholderForProperty(propertySymbol: Symbol, context: NodeBuilderContext) {
5146+ // Use placeholders for reverse mapped types we've either already descended into, or which
5147+ // are nested reverse mappings within a mapping over a non-anonymous type. The later is a restriction mostly just to
5148+ // reduce the blowup in printback size from doing, eg, a deep reverse mapping over `Window`.
5149+ // Since anonymous types usually come from expressions, this allows us to preserve the output
5150+ // for deep mappings which likely come from expressions, while truncating those parts which
5151+ // come from mappings over library functions.
5152+ return !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped)
5153+ && (
5154+ contains(context.reverseMappedStack, propertySymbol as ReverseMappedSymbol)
5155+ || (
5156+ context.reverseMappedStack?.[0]
5157+ && !(getObjectFlags(last(context.reverseMappedStack).propertyType) & ObjectFlags.Anonymous)
5158+ )
5159+ );
5160+ }
5161+
51455162 function addPropertyToElementList(propertySymbol: Symbol, context: NodeBuilderContext, typeElements: TypeElement[]) {
51465163 const propertyIsReverseMapped = !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped);
5147- const propertyType = propertyIsReverseMapped && context.flags & NodeBuilderFlags.InReverseMappedType ?
5164+ const propertyType = shouldUsePlaceholderForProperty(propertySymbol, context) ?
51485165 anyType : getTypeOfSymbol(propertySymbol);
51495166 const saveEnclosingDeclaration = context.enclosingDeclaration;
51505167 context.enclosingDeclaration = undefined;
@@ -5175,16 +5192,20 @@ namespace ts {
51755192 }
51765193 }
51775194 else {
5178- const savedFlags = context.flags;
5179- context.flags |= propertyIsReverseMapped ? NodeBuilderFlags.InReverseMappedType : 0;
51805195 let propertyTypeNode: TypeNode;
5181- if (propertyIsReverseMapped && !!(savedFlags & NodeBuilderFlags.InReverseMappedType )) {
5196+ if (shouldUsePlaceholderForProperty(propertySymbol, context )) {
51825197 propertyTypeNode = createElidedInformationPlaceholder(context);
51835198 }
51845199 else {
5200+ if (propertyIsReverseMapped) {
5201+ context.reverseMappedStack ||= [];
5202+ context.reverseMappedStack.push(propertySymbol as ReverseMappedSymbol);
5203+ }
51855204 propertyTypeNode = propertyType ? serializeTypeForDeclaration(context, propertyType, propertySymbol, saveEnclosingDeclaration) : factory.createKeywordTypeNode(SyntaxKind.AnyKeyword);
5205+ if (propertyIsReverseMapped) {
5206+ context.reverseMappedStack!.pop();
5207+ }
51865208 }
5187- context.flags = savedFlags;
51885209
51895210 const modifiers = isReadonlySymbol(propertySymbol) ? [factory.createToken(SyntaxKind.ReadonlyKeyword)] : undefined;
51905211 if (modifiers) {
@@ -7716,6 +7737,7 @@ namespace ts {
77167737 typeParameterNamesByText?: Set<string>;
77177738 usedSymbolNames?: Set<string>;
77187739 remappedSymbolNames?: ESMap<SymbolId, string>;
7740+ reverseMappedStack?: ReverseMappedSymbol[];
77197741 }
77207742
77217743 function isDefaultBindingContext(location: Node) {
@@ -10870,6 +10892,14 @@ namespace ts {
1087010892 }
1087110893 }
1087210894
10895+ type ReplaceableIndexedAccessType = IndexedAccessType & { objectType: TypeParameter, indexType: TypeParameter };
10896+ function replaceIndexedAccess(instantiable: Type, type: ReplaceableIndexedAccessType, replacement: Type) {
10897+ // map type.indexType to 0
10898+ // map type.objectType to `[TReplacement]`
10899+ // thus making the indexed access `[TReplacement][0]` or `TReplacement`
10900+ return instantiateType(instantiable, createTypeMapper([type.indexType, type.objectType], [getLiteralType(0), createTupleType([replacement])]));
10901+ }
10902+
1087310903 function getIndexInfoOfIndexSymbol(indexSymbol: Symbol, indexKind: IndexKind) {
1087410904 const declaration = getIndexDeclarationOfIndexSymbol(indexSymbol, indexKind);
1087510905 if (!declaration) return undefined;
@@ -10890,8 +10920,21 @@ namespace ts {
1089010920 inferredProp.declarations = prop.declarations;
1089110921 inferredProp.nameType = getSymbolLinks(prop).nameType;
1089210922 inferredProp.propertyType = getTypeOfSymbol(prop);
10893- inferredProp.mappedType = type.mappedType;
10894- inferredProp.constraintType = type.constraintType;
10923+ if (type.constraintType.type.flags & TypeFlags.IndexedAccess
10924+ && (type.constraintType.type as IndexedAccessType).objectType.flags & TypeFlags.TypeParameter
10925+ && (type.constraintType.type as IndexedAccessType).indexType.flags & TypeFlags.TypeParameter) {
10926+ // A reverse mapping of `{[K in keyof T[K_1]]: T[K_1]}` is the same as that of `{[K in keyof T]: T}`, since all we care about is
10927+ // inferring to the "type parameter" (or indexed access) shared by the constraint and template. So, to reduce the number of
10928+ // type identities produced, we simplify such indexed access occurences
10929+ const newTypeParam = (type.constraintType.type as IndexedAccessType).objectType;
10930+ const newMappedType = replaceIndexedAccess(type.mappedType, type.constraintType.type as ReplaceableIndexedAccessType, newTypeParam);
10931+ inferredProp.mappedType = newMappedType as MappedType;
10932+ inferredProp.constraintType = getIndexType(newTypeParam) as IndexType;
10933+ }
10934+ else {
10935+ inferredProp.mappedType = type.mappedType;
10936+ inferredProp.constraintType = type.constraintType;
10937+ }
1089510938 members.set(prop.escapedName, inferredProp);
1089610939 }
1089710940 setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
@@ -20611,7 +20654,11 @@ namespace ts {
2061120654 }
2061220655
2061320656 function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol) {
20614- return inferReverseMappedType(symbol.propertyType, symbol.mappedType, symbol.constraintType);
20657+ const links = getSymbolLinks(symbol);
20658+ if (!links.type) {
20659+ links.type = inferReverseMappedType(symbol.propertyType, symbol.mappedType, symbol.constraintType);
20660+ }
20661+ return links.type;
2061520662 }
2061620663
2061720664 function inferReverseMappedType(sourceType: Type, target: MappedType, constraint: IndexType): Type {
0 commit comments