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