Skip to content

Commit dd47fff

Browse files
committed
Properly handle recursive array and tuple types in printback
1 parent 52a1a14 commit dd47fff

File tree

1 file changed

+36
-32
lines changed

1 file changed

+36
-32
lines changed

src/compiler/checker.ts

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3540,7 +3540,6 @@ namespace ts {
35403540
}
35413541

35423542
function createNodeBuilder() {
3543-
let depth = 0;
35443543
return {
35453544
typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) =>
35463545
withContext(enclosingDeclaration, flags, tracker, context => typeToTypeNodeHelper(type, context)),
@@ -3712,7 +3711,7 @@ namespace ts {
37123711

37133712
if (objectFlags & ObjectFlags.Reference) {
37143713
Debug.assert(!!(type.flags & TypeFlags.Object));
3715-
return typeReferenceToTypeNode(<TypeReference>type);
3714+
return (<TypeReference>type).node ? visitAndTransformType(type, typeReferenceToTypeNode) : typeReferenceToTypeNode(<TypeReference>type);
37163715
}
37173716
if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) {
37183717
if (type.flags & TypeFlags.TypeParameter && contains(context.inferTypeParameters, type)) {
@@ -3804,10 +3803,7 @@ namespace ts {
38043803
function createAnonymousTypeNode(type: ObjectType): TypeNode {
38053804
const typeId = "" + type.id;
38063805
const symbol = type.symbol;
3807-
let id: string;
38083806
if (symbol) {
3809-
const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class;
3810-
id = (isConstructorObject ? "+" : "") + getSymbolId(symbol);
38113807
if (isJSConstructor(symbol.valueDeclaration)) {
38123808
// Instance and static types share the same symbol; only add 'typeof' for the static side.
38133809
const isInstanceType = type === getInferredClassType(symbol) ? SymbolFlags.Type : SymbolFlags.Value;
@@ -3831,25 +3827,7 @@ namespace ts {
38313827
}
38323828
}
38333829
else {
3834-
// Since instantiations of the same anonymous type have the same symbol, tracking symbols instead
3835-
// of types allows us to catch circular references to instantiations of the same anonymous type
3836-
if (!context.visitedTypes) {
3837-
context.visitedTypes = createMap<true>();
3838-
}
3839-
if (!context.symbolDepth) {
3840-
context.symbolDepth = createMap<number>();
3841-
}
3842-
3843-
const depth = context.symbolDepth.get(id) || 0;
3844-
if (depth > 10) {
3845-
return createElidedInformationPlaceholder(context);
3846-
}
3847-
context.symbolDepth.set(id, depth + 1);
3848-
context.visitedTypes.set(typeId, true);
3849-
const result = createTypeNodeFromObjectType(type);
3850-
context.visitedTypes.delete(typeId);
3851-
context.symbolDepth.set(id, depth);
3852-
return result;
3830+
return visitAndTransformType(type, createTypeNodeFromObjectType);
38533831
}
38543832
}
38553833
else {
@@ -3871,6 +3849,37 @@ namespace ts {
38713849
}
38723850
}
38733851

3852+
function visitAndTransformType<T>(type: Type, transform: (type: Type) => T) {
3853+
const typeId = "" + type.id;
3854+
const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class;
3855+
const id = getObjectFlags(type) & ObjectFlags.Reference ? "N" + getNodeId((<DeferredTypeReference>type).node) :
3856+
(isConstructorObject ? "+" : "") + getSymbolId(type.symbol);
3857+
// Since instantiations of the same anonymous type have the same symbol, tracking symbols instead
3858+
// of types allows us to catch circular references to instantiations of the same anonymous type
3859+
if (!context.visitedTypes) {
3860+
context.visitedTypes = createMap<true>();
3861+
}
3862+
if (id && !context.symbolDepth) {
3863+
context.symbolDepth = createMap<number>();
3864+
}
3865+
3866+
let depth: number | undefined;
3867+
if (id) {
3868+
depth = context.symbolDepth!.get(id) || 0;
3869+
if (depth > 10) {
3870+
return createElidedInformationPlaceholder(context);
3871+
}
3872+
context.symbolDepth!.set(id, depth + 1);
3873+
}
3874+
context.visitedTypes.set(typeId, true);
3875+
const result = transform(type);
3876+
context.visitedTypes.delete(typeId);
3877+
if (id) {
3878+
context.symbolDepth!.set(id, depth!);
3879+
}
3880+
return result;
3881+
}
3882+
38743883
function createTypeNodeFromObjectType(type: ObjectType): TypeNode {
38753884
if (isGenericMappedType(type)) {
38763885
return createMappedTypeNodeFromType(type);
@@ -3910,15 +3919,10 @@ namespace ts {
39103919
const typeArguments: ReadonlyArray<Type> = getTypeArguments(type);
39113920
if (type.target === globalArrayType || type.target === globalReadonlyArrayType) {
39123921
if (context.flags & NodeBuilderFlags.WriteArrayAsGenericType) {
3913-
depth++;
3914-
const typeArgumentNode = typeToTypeNodeHelper(depth >= 5 ? anyType : typeArguments[0], context);
3915-
depth--;
3916-
createTypeReferenceNode(type.target === globalArrayType ? "Array" : "ReadonlyArray", [typeArgumentNode]);
3922+
const typeArgumentNode = typeToTypeNodeHelper(typeArguments[0], context);
3923+
return createTypeReferenceNode(type.target === globalArrayType ? "Array" : "ReadonlyArray", [typeArgumentNode]);
39173924
}
3918-
3919-
depth++;
3920-
const elementType = typeToTypeNodeHelper(depth >= 5 ? anyType : typeArguments[0], context);
3921-
depth--;
3925+
const elementType = typeToTypeNodeHelper(typeArguments[0], context);
39223926
const arrayType = createArrayTypeNode(elementType);
39233927
return type.target === globalArrayType ? arrayType : createTypeOperatorNode(SyntaxKind.ReadonlyKeyword, arrayType);
39243928
}

0 commit comments

Comments
 (0)