diff --git a/CHANGELOG.md b/CHANGELOG.md index bc0192bb90..7cc20893b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Bug Fixes - Fixed an issue where a namespace would not be created for merged function-namespaces only containing types, #2476. +- Fixed an infinite loop when converting a union type which directly contained another union type which refers to itself, #2469. ## v0.25.6 (2024-01-01) diff --git a/src/lib/converter/types.ts b/src/lib/converter/types.ts index 7de8e0cbd2..2c8f589a8d 100644 --- a/src/lib/converter/types.ts +++ b/src/lib/converter/types.ts @@ -136,12 +136,7 @@ export function convertType( // We need to check it here, not just in the union checker, because typeToTypeNode // will use the origin when serializing // aliasSymbol check is important - #2468 - if ( - typeOrNode.isUnion() && - typeOrNode.origin && - !typeOrNode.origin.isUnion() && - !typeOrNode.aliasSymbol - ) { + if (typeOrNode.isUnion() && typeOrNode.origin && !typeOrNode.aliasSymbol) { return convertType(context, typeOrNode.origin); } diff --git a/src/test/TestLogger.ts b/src/test/TestLogger.ts index 313e8b2a19..b1d63a0c85 100644 --- a/src/test/TestLogger.ts +++ b/src/test/TestLogger.ts @@ -32,6 +32,17 @@ export class TestLogger extends Logger { this.messages.splice(index, 1); } + expectNoMessage(message: string) { + const regex = createRegex(message); + const index = this.messages.findIndex((m) => regex.test(m)); + if (index !== -1) { + const messages = this.messages.join("\n\t"); + fail( + `Expected "${message}" to not be logged. The logged messages were:\n\t${messages}`, + ); + } + } + expectNoOtherMessages({ ignoreDebug } = { ignoreDebug: true }) { const messages = ignoreDebug ? this.messages.filter((msg) => !msg.startsWith("debug")) diff --git a/src/test/behavior.c2.test.ts b/src/test/behavior.c2.test.ts index ddec3b2f5e..8e81a62379 100644 --- a/src/test/behavior.c2.test.ts +++ b/src/test/behavior.c2.test.ts @@ -1086,12 +1086,15 @@ describe("Behavior Tests", () => { logger.expectNoOtherMessages(); }); - it("Should not produce warnings when processing an object type twice due to intersection", () => { + it("Should not warn about recursive types", () => { const project = convert("refusingToRecurse"); const schemaTypeBased = query(project, "schemaTypeBased"); equal(schemaTypeBased.type?.toString(), "Object & Object"); + equal( + querySig(project, "Map.getFilter").type?.toString(), + "void | ExpressionSpecification", + ); - logger.expectMessage("debug: Begin readme.md*"); - logger.expectNoOtherMessages({ ignoreDebug: false }); + logger.expectNoMessage("debug: Refusing to recurse*"); }); }); diff --git a/src/test/converter/variables/specs.json b/src/test/converter/variables/specs.json index cc2390a045..e733b6d1d5 100644 --- a/src/test/converter/variables/specs.json +++ b/src/test/converter/variables/specs.json @@ -925,8 +925,10 @@ } ], "type": { - "type": "unknown", - "name": "{ success: (successCallback: () => any) => ...; error: (errorCallback: () => any) => ...; finally: (finallyCallback: () => any) => ...; }" + "type": "reference", + "target": 55, + "name": "__object", + "package": "typedoc" } } ] @@ -1028,8 +1030,10 @@ } ], "type": { - "type": "unknown", - "name": "{ success: (successCallback: () => any) => ...; error: (errorCallback: () => any) => ...; finally: (finallyCallback: () => any) => ...; }" + "type": "reference", + "target": 55, + "name": "__object", + "package": "typedoc" } } ] @@ -1131,8 +1135,10 @@ } ], "type": { - "type": "unknown", - "name": "{ success: (successCallback: () => any) => ...; error: (errorCallback: () => any) => ...; finally: (finallyCallback: () => any) => ...; }" + "type": "reference", + "target": 55, + "name": "__object", + "package": "typedoc" } } ] diff --git a/src/test/converter2/behavior/refusingToRecurse.ts b/src/test/converter2/behavior/refusingToRecurse.ts index 1de022e82f..6eb6719d05 100644 --- a/src/test/converter2/behavior/refusingToRecurse.ts +++ b/src/test/converter2/behavior/refusingToRecurse.ts @@ -27,3 +27,17 @@ export const schema = { export type Schema = FromSchema; export const schemaTypeBased = null! as Schema; + +export type ExpressionSpecification = + | ["array", unknown | ExpressionSpecification] + | [ + "array", + string | ExpressionSpecification, + unknown | ExpressionSpecification, + ]; + +export class Map { + getFilter(layerId: string): ExpressionSpecification | void { + return; + } +}