Validate field in discriminated union based on other field in zod
Publish date: 2024-05-22
I have the following discriminated union:
enum Option { FULL = 'FULL', TIME_PERIOD = 'TIME_PERIOD', MONTH = 'MONTH', } const schema = z.discriminatedUnion('option', [ z.object({ option: z.literal(Option.FULL) }), z.object({ option: z.literal(Option.TIME_PERIOD), from: z.date(), to: z.date(), }), z.object({ option: z.literal(Option.MONTH), month: z.date() }), ])
Now I want to refine the second object, to check if the date from
is before to
:
const schema = z.discriminatedUnion('option', [ z.object({ option: z.literal(Option.FULL) }), z.object({ option: z.literal(Option.TIME_PERIOD), from: z.date(), to: z.date(), }).refine( ({ from, to }) => isBefore(from, to), { message: '"from" must be before "to"', path: ['from'], } ), z.object({ option: z.literal(Option.MONTH), month: z.date() }), ])
But this gives me an error, basically saying "type ZodEffects is not allowed as ZodDiscriminatedUnionOption".
How can I achieve the desired behaivior in validation?
Here is a codesanbox of my problem: https://codesandbox.io/s/zod-refine-in-discriminated-union-5kve15
1 Answer
The solution is to refine the discriminated union itself, not the option inside the union:
const schema = z.discriminatedUnion('option', [ z.object({ option: z.literal(Option.FULL) }), z.object({ option: z.literal(Option.TIME_PERIOD), from: z.date(), to: z.date(), }), z.object({ option: z.literal(Option.MONTH), month: z.date() }), ]).refine( (data) => { if (data.option === Option.TIME_PERIOD) { return isBefore(data.from, data.to) } return true }, { message: '"from" must be before "to"', path: ['from'], } )
ncG1vNJzZmirpJawrLvVnqmfpJ%2Bse6S7zGiorp2jqbawutJobm1vaWWCd4COr5iloZSWwaZ5xaKcpZxdnrtusMismquhnZ67osDEnWSuppmku26uwKycnWWfo3qwwMeeqWaemZq5pXnIp2Szp5Q%3D