Capítulo 17 de 61
Self-referential and mutually-recursive schemas are defined using a JS getter on the recursive key, letting the reference resolve lazily at runtime instead of at schema-definition time.
get fieldName() { return schema } instead of a plain property, so the reference to the (not-yet-fully-defined) schema resolves lazily.z.union([...]), z.optional(SomeArray)) often breaks TypeScript's inference.implicitly has return type 'any'); fix by adding an explicit return-type annotation on the getter.const Category = z.object({
name: z.string(),
get subcategories() {
return z.array(Category);
},
});
// type Category = { name: string; subcategories: Category[] }
// mutually recursive types
const User = z.object({
email: z.email(),
get posts() { return z.array(Post); },
});
const Post = z.object({
title: z.string(),
get author() { return User; },
});
// fixing a circularity type error with an explicit annotation
const Activity = z.object({
name: z.string(),
get subactivities(): z.ZodNullable<z.ZodArray<typeof Activity>> {
return z.nullable(z.array(Activity));
},
});
.parse() causes an infinite loop.z.union([z.null(), Activity]) inside a getter is harder for TypeScript to infer than an equivalent method chain — prefer methods over standalone functions in recursive contexts, especially in Zod Mini..pick(), .omit(), .partial(), etc.) still work normally on recursive schemas.