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 nestedZodOptional. 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 subclassZodType` 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
MergeZodObjectsDeepis too complicated and limited. I regret adding.deepPartialbecause 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
.deepPartialand in your PRisn't smart enough to properly handle the nestedZodOptional. In fact to be fully complete you'd need to account for every possible subclass ofZodTypewhich isn't possible because Zod is designed to be extensible! People can subclassZodType` if they want and create new schema types.Anyway I'm very allergic to things like this since
deepPartialanddiscriminatedUnionhave 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.