@@ -5058,9 +5058,21 @@ namespace ts {
50585058 return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword);
50595059 }
50605060
5061+ function shouldUsePlaceholderForProperty(propertySymbol: Symbol, context: NodeBuilderContext) {
5062+ // Use placeholders for reverse mapped types we've either already descended into, or which
5063+ // are nested reverse mappings within a mapping over a non-anonymous type. The later is a restriction mostly just to
5064+ // reduce the blowup in printback size from doing, eg, a deep reverse mapping over `Window`.
5065+ // Since anonymous types usually come from expressions, this allows us to preserve the output
5066+ // for deep mappings which likely come from expressions, while truncating those parts which
5067+ // come from mappings over library functions.
5068+ return !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped) && (
5069+ contains(context.reverseMappedStack, propertySymbol as ReverseMappedSymbol)
5070+ || (context.reverseMappedStack?.[0] && !(getObjectFlags(last(context.reverseMappedStack).propertyType) & ObjectFlags.Anonymous)));
5071+ }
5072+
50615073 function addPropertyToElementList(propertySymbol: Symbol, context: NodeBuilderContext, typeElements: TypeElement[]) {
50625074 const propertyIsReverseMapped = !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped);
5063- const propertyType = propertyIsReverseMapped && context.flags & NodeBuilderFlags.InReverseMappedType ?
5075+ const propertyType = shouldUsePlaceholderForProperty(propertySymbol, context) ?
50645076 anyType : getTypeOfSymbol(propertySymbol);
50655077 const saveEnclosingDeclaration = context.enclosingDeclaration;
50665078 context.enclosingDeclaration = undefined;
@@ -5090,16 +5102,20 @@ namespace ts {
50905102 }
50915103 }
50925104 else {
5093- const savedFlags = context.flags;
5094- context.flags |= propertyIsReverseMapped ? NodeBuilderFlags.InReverseMappedType : 0;
50955105 let propertyTypeNode: TypeNode;
5096- if (propertyIsReverseMapped && !!(savedFlags & NodeBuilderFlags.InReverseMappedType )) {
5106+ if (shouldUsePlaceholderForProperty(propertySymbol, context )) {
50975107 propertyTypeNode = createElidedInformationPlaceholder(context);
50985108 }
50995109 else {
5110+ if (propertyIsReverseMapped) {
5111+ context.reverseMappedStack ||= [];
5112+ context.reverseMappedStack.push(propertySymbol as ReverseMappedSymbol);
5113+ }
51005114 propertyTypeNode = propertyType ? serializeTypeForDeclaration(context, propertyType, propertySymbol, saveEnclosingDeclaration) : factory.createKeywordTypeNode(SyntaxKind.AnyKeyword);
5115+ if (propertyIsReverseMapped) {
5116+ context.reverseMappedStack!.pop();
5117+ }
51015118 }
5102- context.flags = savedFlags;
51035119
51045120 const modifiers = isReadonlySymbol(propertySymbol) ? [factory.createToken(SyntaxKind.ReadonlyKeyword)] : undefined;
51055121 if (modifiers) {
@@ -7608,6 +7624,7 @@ namespace ts {
76087624 typeParameterNamesByText?: Set<string>;
76097625 usedSymbolNames?: Set<string>;
76107626 remappedSymbolNames?: ESMap<SymbolId, string>;
7627+ reverseMappedStack?: ReverseMappedSymbol[];
76117628 }
76127629
76137630 function isDefaultBindingContext(location: Node) {
@@ -10659,6 +10676,14 @@ namespace ts {
1065910676 }
1066010677 }
1066110678
10679+ type ReplaceableIndexedAccessType = IndexedAccessType & { objectType: TypeParameter, indexType: TypeParameter };
10680+ function replaceIndexedAccess(instantiable: Type, type: ReplaceableIndexedAccessType, replacement: Type) {
10681+ // map type.indexType to 0
10682+ // map type.objectType to `[TReplacement]`
10683+ // thus making the indexed access `[TReplacement][0]` or `TReplacement`
10684+ return instantiateType(instantiable, createTypeMapper([type.indexType, type.objectType], [getLiteralType(0), createTupleType([replacement])]));
10685+ }
10686+
1066210687 function resolveReverseMappedTypeMembers(type: ReverseMappedType) {
1066310688 const indexInfo = getIndexInfoOfType(type.source, IndexKind.String);
1066410689 const modifiers = getMappedTypeModifiers(type.mappedType);
@@ -10672,8 +10697,21 @@ namespace ts {
1067210697 inferredProp.declarations = prop.declarations;
1067310698 inferredProp.nameType = getSymbolLinks(prop).nameType;
1067410699 inferredProp.propertyType = getTypeOfSymbol(prop);
10675- inferredProp.mappedType = type.mappedType;
10676- inferredProp.constraintType = type.constraintType;
10700+ if (type.constraintType.type.flags & TypeFlags.IndexedAccess
10701+ && (type.constraintType.type as IndexedAccessType).objectType.flags & TypeFlags.TypeParameter
10702+ && (type.constraintType.type as IndexedAccessType).indexType.flags & TypeFlags.TypeParameter) {
10703+ // 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
10704+ // inferring to the "type parameter" (or indexed access) shared by the constraint and template. So, to reduce the number of
10705+ // type identities produced, we simplify such indexed access occurences
10706+ const newTypeParam = (type.constraintType.type as IndexedAccessType).objectType;
10707+ const newMappedType = replaceIndexedAccess(type.mappedType, type.constraintType.type as ReplaceableIndexedAccessType, newTypeParam);
10708+ inferredProp.mappedType = newMappedType as MappedType;
10709+ inferredProp.constraintType = getIndexType(newTypeParam) as IndexType;
10710+ }
10711+ else {
10712+ inferredProp.mappedType = type.mappedType;
10713+ inferredProp.constraintType = type.constraintType;
10714+ }
1067710715 members.set(prop.escapedName, inferredProp);
1067810716 }
1067910717 setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
@@ -20249,7 +20287,11 @@ namespace ts {
2024920287 }
2025020288
2025120289 function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol) {
20252- return inferReverseMappedType(symbol.propertyType, symbol.mappedType, symbol.constraintType);
20290+ const links = getSymbolLinks(symbol);
20291+ if (!links.type) {
20292+ links.type = inferReverseMappedType(symbol.propertyType, symbol.mappedType, symbol.constraintType);
20293+ }
20294+ return links.type;
2025320295 }
2025420296
2025520297 function inferReverseMappedType(sourceType: Type, target: MappedType, constraint: IndexType): Type {
0 commit comments