@@ -5142,9 +5142,26 @@ namespace ts {
5142
5142
return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword);
5143
5143
}
5144
5144
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
+
5145
5162
function addPropertyToElementList(propertySymbol: Symbol, context: NodeBuilderContext, typeElements: TypeElement[]) {
5146
5163
const propertyIsReverseMapped = !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped);
5147
- const propertyType = propertyIsReverseMapped && context.flags & NodeBuilderFlags.InReverseMappedType ?
5164
+ const propertyType = shouldUsePlaceholderForProperty(propertySymbol, context) ?
5148
5165
anyType : getTypeOfSymbol(propertySymbol);
5149
5166
const saveEnclosingDeclaration = context.enclosingDeclaration;
5150
5167
context.enclosingDeclaration = undefined;
@@ -5175,16 +5192,20 @@ namespace ts {
5175
5192
}
5176
5193
}
5177
5194
else {
5178
- const savedFlags = context.flags;
5179
- context.flags |= propertyIsReverseMapped ? NodeBuilderFlags.InReverseMappedType : 0;
5180
5195
let propertyTypeNode: TypeNode;
5181
- if (propertyIsReverseMapped && !!(savedFlags & NodeBuilderFlags.InReverseMappedType )) {
5196
+ if (shouldUsePlaceholderForProperty(propertySymbol, context )) {
5182
5197
propertyTypeNode = createElidedInformationPlaceholder(context);
5183
5198
}
5184
5199
else {
5200
+ if (propertyIsReverseMapped) {
5201
+ context.reverseMappedStack ||= [];
5202
+ context.reverseMappedStack.push(propertySymbol as ReverseMappedSymbol);
5203
+ }
5185
5204
propertyTypeNode = propertyType ? serializeTypeForDeclaration(context, propertyType, propertySymbol, saveEnclosingDeclaration) : factory.createKeywordTypeNode(SyntaxKind.AnyKeyword);
5205
+ if (propertyIsReverseMapped) {
5206
+ context.reverseMappedStack!.pop();
5207
+ }
5186
5208
}
5187
- context.flags = savedFlags;
5188
5209
5189
5210
const modifiers = isReadonlySymbol(propertySymbol) ? [factory.createToken(SyntaxKind.ReadonlyKeyword)] : undefined;
5190
5211
if (modifiers) {
@@ -7716,6 +7737,7 @@ namespace ts {
7716
7737
typeParameterNamesByText?: Set<string>;
7717
7738
usedSymbolNames?: Set<string>;
7718
7739
remappedSymbolNames?: ESMap<SymbolId, string>;
7740
+ reverseMappedStack?: ReverseMappedSymbol[];
7719
7741
}
7720
7742
7721
7743
function isDefaultBindingContext(location: Node) {
@@ -10870,6 +10892,14 @@ namespace ts {
10870
10892
}
10871
10893
}
10872
10894
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
+
10873
10903
function getIndexInfoOfIndexSymbol(indexSymbol: Symbol, indexKind: IndexKind) {
10874
10904
const declaration = getIndexDeclarationOfIndexSymbol(indexSymbol, indexKind);
10875
10905
if (!declaration) return undefined;
@@ -10890,8 +10920,21 @@ namespace ts {
10890
10920
inferredProp.declarations = prop.declarations;
10891
10921
inferredProp.nameType = getSymbolLinks(prop).nameType;
10892
10922
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
+ }
10895
10938
members.set(prop.escapedName, inferredProp);
10896
10939
}
10897
10940
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
@@ -20611,7 +20654,11 @@ namespace ts {
20611
20654
}
20612
20655
20613
20656
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;
20615
20662
}
20616
20663
20617
20664
function inferReverseMappedType(sourceType: Type, target: MappedType, constraint: IndexType): Type {
0 commit comments