discriminated union cant be nested
Currently it is impossible to allow discriminated unions as argument to another discriminated union. Typescript however allows that so it should be allowed in zod as well
Example Typescript and zod: https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgLzgXzgMyhEcDkyEAJvgLABQlMAnmAKZwCCcAvIpXHMcAM4DGUUMAB2AQxjQAjAC44MKAFd6Abk5wQvAOZzeC0VrUU0lanUYAhFuwTqwYqGJCy4IxSABG9KEa48BQiCiEtAATHKYYgA2vKrq-oLC4pJQLpEx9JQmVBS0DHAWFmwcFH58iUHJYXIKyr5w9o4g4a7uXj7x5YHBKWnRsVmmueYFxQAUVnAAPgUWAJRDeYwAwsUsMxZDlPwQInpYIuNgcstzbAB8JerAmHBjAIRgAHQJ3VWpZ7alXHA3d48vLpJEJQUKfdQ-OA7PYQKL0J5RCBaMbPRpOKQLb5cDD0DIlSFcaG8WHwxHI1EOJxgiHodQ4vFfSFEkkIpEop6aLSY7GDHJE+AAfWsKCeEA8ACt6PwYGNGXAoPRJdL6MQ5MgEcAYN5omNavQ5gAadSctVPPRCETIzFoTHbXb7AWTdjqsVKmVynj0ZaKGDLCB6XimtyebxjQ3qBWKWKqkVRTXaqJjdKxcPfBVulWmuNaxyJ5P6rK2ij8uCOorO0USqXu9QAdwAFhIg21Q6muJHo1n47ndUp9Ua04rq5nY92dfnrUWS47iurXsCtcQAKoiYC7Mb4Dsq-AGuAAbUdTF3ZYAukWAPTnuDeHBQXh2vaC1YV+eVCQqldrkQb9PD0i7g8j1LCwzyGS9fhETBvGAuBa2gABre9hnyAAtFDZyeUQoKgAAeJYIFuR1zkoIA
Here is the code for reference:
import { z } from 'zod'
type A = {
discriminator1: true;
msg: string;
}
type BA = {
param1: number;
discriminator2: false;
discriminator1: false
}
type BB = {
discriminator2: true;
param2: number;
discriminator1: false
}
type B = (BA | BB)
type C = A | B
const fn = (p: C) => {
if (!p.discriminator1) {
if (!p.discriminator2) {
console.log(p.param1)
} else {
console.log(p.param2)
}
} else {
console.log(p.msg)
}
}
const _A = z.object({
rejected: z.literal(true),
msg: z.string()
})
const _BA = z.object({
dieCutCosts: z.number(),
reused: z.literal(false),
rejected: z.literal(false)
})
const _BB = z.object({
what: z.number(),
reused: z.literal(true),
rejected: z.literal(false)
})
const _B = z.discriminatedUnion('reused', [_BA, _BB])
// errors
const _C = z.discriminatedUnion('rejected', [_A, _B])
// infer _B works
type ZZ = z.infer<typeof _B>
+1
Any updates on this issue?
Bumping this. Any updates?
Bumping
Hi all, this is addressed in Zod v4 feel free to have a look here: https://v4.zod.dev/, if this answers your question, please could you close the issue?
Zod 4 does support nested discriminated unions, but there are some important requirements and recent changes to be aware of. The key is that the discriminator key (like "rejected" or "reused") must be present on the final object value for each variant, even when nesting. Recent updates have added tests and improved support for these patterns, so if you're on Zod 4, this should work as long as your schemas are structured so that the discriminator is always available at the top level of each variant [PR #3956] [nested union tests].
If you see an error when nesting discriminated unions, it's usually because one of the variants doesn't have the required discriminator key at the top level, or because an older version of Zod is being used. Make sure you're on the latest Zod 4 release.
If you want to merge options from multiple discriminated unions (with the same discriminator key), you can use the .options property to combine them into a new discriminated union, as shown in the v3 docs [v3 docs example].
If your question is answered, please close the issue! If you still see errors with the latest Zod, please share a minimal reproduction (including your Zod version and the exact error message) so @colinhacks can take a closer look.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other