From 723a7a1a69daf00a14be60fd4872e664e9583f06 Mon Sep 17 00:00:00 2001 From: Jure Rotar Date: Fri, 14 Aug 2026 09:55:04 +0200 Subject: [PATCH] fix: circular deps fix --- src/generators/core/zod/getZodSchema.ts | 2 +- .../generate/generateModels.test.ts | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/generators/core/zod/getZodSchema.ts b/src/generators/core/zod/getZodSchema.ts index f986331f..3849a5b1 100644 --- a/src/generators/core/zod/getZodSchema.ts +++ b/src/generators/core/zod/getZodSchema.ts @@ -149,7 +149,7 @@ export function getZodSchema({ schema, resolver, meta: inheritedMeta, tag }: Get properties = `{ ${propsMap .map(([prop, propSchema, isCircular]) => isCircular - ? `get ${wrapWithQuotesIfNeeded(prop!)}() { return ${propSchema} }` + ? `${wrapWithQuotesIfNeeded(prop!)}: z.lazy(() => ${propSchema})` : `${wrapWithQuotesIfNeeded(prop!)}: ${propSchema}`, ) .join(", ")} }`; diff --git a/src/generators/generate/generateModels.test.ts b/src/generators/generate/generateModels.test.ts index ab0395c1..b75a2f65 100644 --- a/src/generators/generate/generateModels.test.ts +++ b/src/generators/generate/generateModels.test.ts @@ -48,4 +48,83 @@ describe("generateModels", () => { expect(models).not.toContain("ZodObject"); expect(models).toContain("export const UnrelatedSchema ="); }); + + test("uses lazy properties for circular refs inside allOf object composition", () => { + const doc = { + openapi: "3.0.3", + info: { title: "AllOf recursive schemas", version: "1.0.0" }, + paths: { + "/team-players": { + get: { + tags: ["teams"], + responses: { + 200: { + description: "ok", + content: { + "application/json": { + schema: { $ref: "#/components/schemas/TeamPlayer" }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: { + Base: { + type: "object", + properties: { + id: { type: "integer" }, + }, + }, + TeamPlayer: { + allOf: [ + { $ref: "#/components/schemas/Base" }, + { + type: "object", + properties: { + Team: { + type: "object", + nullable: true, + allOf: [{ $ref: "#/components/schemas/SeasonTeam" }], + }, + }, + }, + ], + }, + SeasonTeam: { + allOf: [ + { $ref: "#/components/schemas/Base" }, + { + type: "object", + properties: { + TeamPlayers: { + type: "array", + nullable: true, + items: { $ref: "#/components/schemas/TeamPlayer" }, + }, + }, + }, + ], + }, + }, + }, + } as OpenAPIV3.Document; + const options = { + ...DEFAULT_GENERATE_OPTIONS, + excludeRedundantZodSchemas: false, + splitByTags: false, + tsNamespaces: false, + replaceOptionalWithNullish: true, + }; + const { resolver, data } = getDataFromOpenAPIDoc(doc, options); + + const models = generateModels({ resolver, data, tag: options.defaultTag }); + + expect(models).toContain("Team: z.lazy(() => SeasonTeamSchema.nullable())"); + expect(models).toContain("TeamPlayers: z.lazy(() => z.array(TeamPlayerSchema).nullable())"); + expect(models).not.toContain("get Team()"); + expect(models).not.toContain("get TeamPlayers()"); + }); });