zod
zod copied to clipboard
feat(#1508): Add mergeDeep to ZodObject
This PR adds mergeDeep
to ZodObject
. It's now possible to merge two ZodObjects
deeply.
The method follows the same rules as merge
—keys of B
have priority over those from A
, and the B
's unknownKeys
and catchall
schema are preserved.
const A = z.object({
a: z.string(),
b: z.object({
c: z.string(),
d: z.string(),
e: z.object({
f: z.string(),
g: z.string(),
h: z.string(),
}),
}),
});
const B = z.object({
b: z.object({
i: z.string(), // should add this prop deeply inside the shape's `b` key
j: z.string(), // same
e: z.object({ // does not completely overwrite this key; instead add properties to it.
k: z.string(), // will be added
l: z.string(), // will be added
h: z.bigint(), // Overwrite from string to bigint
}),
}),
});
const mergedDeep = A.mergeDeep(B);

Deploy Preview for guileless-rolypoly-866f8a ready!
Built without sensitive environment variables
Name | Link |
---|---|
Latest commit | 22dcd1a71630bf129e139736daff381b272278af |
Latest deploy log | https://app.netlify.com/sites/guileless-rolypoly-866f8a/deploys/63a416cc4b3f6e00073f97f2 |
Deploy Preview | https://deploy-preview-1739--guileless-rolypoly-866f8a.netlify.app |
Preview on mobile | Toggle QR Code...Use your smartphone camera to open QR code link. |
To edit notification comments on pull requests, go to your Netlify site settings.
Just because it can be done doesn't mean it should. The logic in MergeZodObjectsDeep
is too complicated and limited. I regret adding .deepPartial
because it remains incomplete and confusing to people who think it will "just work" when you have something like this:
z.object({
outer: z.object({
inner: z.string()
}).optional()
})
The logic (both in .deepPartial
and in your PRisn't smart enough to properly handle the nested
ZodOptional. In fact to be fully complete you'd need to account for every possible subclass of
ZodTypewhich isn't possible because Zod is designed to be extensible! People can subclass
ZodType` if they want and create new schema types.
Anyway I'm very allergic to things like this since deepPartial
and discriminatedUnion
have been a huge burden and a point of confusion for a long time.
Just because it can be done doesn't mean it should. The logic in
MergeZodObjectsDeep
is too complicated and limited. I regret adding.deepPartial
because it remains incomplete and confusing to people who think it will "just work" when you have something like this:z.object({ outer: z.object({ inner: z.string() }).optional() })
The logic (both in
.deepPartial
and in your PRisn't smart enough to properly handle the nested
ZodOptional. In fact to be fully complete you'd need to account for every possible subclass of
ZodTypewhich isn't possible because Zod is designed to be extensible! People can subclass
ZodType` if they want and create new schema types.Anyway I'm very allergic to things like this since
deepPartial
anddiscriminatedUnion
have been a huge burden and a point of confusion for a long time.
Closing PR and no more deep things 😢 But your concern is totally valid.