SebKas
SebKas

Reputation: 554

Validate field in discriminated union based on other field in zod

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

Upvotes: 7

Views: 8418

Answers (1)

SebKas
SebKas

Reputation: 554

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'],
  }
)

Upvotes: 13

Related Questions