colinhacks / zod

TypeScript-first schema validation with static type inference
https://zod.dev
MIT License
33.98k stars 1.19k forks source link

Does anyone know how to conditionally validate form fields? #3561

Open badddams opened 5 months ago

badddams commented 5 months ago

I've been using Zod for about 2 weeks now and I spend way more time googling how to do stuff.

I'm implementing basic form functionality yet Zod feels like it makes it 1000 times more difficult than it needs to be.

I have a form that contains

I want to make the end date required only when the checkbox is false. I am using react-hook-form + zod. Can anyone help?

patrickJramos commented 5 months ago

you can just refine the object, or if you want a better type on the end, use discriminatedUnion

using refine:

const validator1 = z.object({
  start_date: z.date(),
  end_date: z.date().optional(),
  checkbox: z.boolean()
}).refine(value => {
  if (value.checkbox) {
    return value.end_date != undefined;
  }
  return true;
});

let result = validator1.safeParse({ checkbox: true, start_date: new Date() });
console.log(result); // error

result = validator1.safeParse({ checkbox: false, start_date: new Date() });
if (result.success) {
  console.log(result.data); // success, type of data is { start_date: Date; checkbox: boolean; end_date?: Date | undefined; }
}

using discriminatedUnion:

const withRequiredEnd = z.object({
  start_date: z.date(),
  end_date: z.date(),
  checkbox: z.literal(true)
});

const withOptionalEnd = z.object({
  start_date: z.date(),
  end_date: z.date().optional(),
  checkbox: z.literal(false)
});

const validator2 = z.discriminatedUnion('checkbox', [withRequiredEnd, withOptionalEnd]);

let result2 = validator2.safeParse({ checkbox: true, start_date: new Date() });
console.log(result2); // error

result2 = validator2.safeParse({ checkbox: false, start_date: new Date() });
if (result2.success) {
  console.log(result2.data); // success, type of data is
  // {
  //     checkbox: true;
  //     start_date: Date;
  //     end_date: Date;
  // } | {
  //     checkbox: false;
  //     start_date: Date;
  //     end_date?: Date | undefined;
  // }
}
jadejr commented 4 months ago

Perhaps there needs to be a cookbook of examples in the codebase somewhere?

jkinley commented 3 months ago

What about if you are not using a checkbox to determine if a field is conditional. There are lots of times in forms when we have to create conditional fields when a select is set at a specific value.