Search code examples
reactjsformsvalidationzod

How to validate Zod Object depending on value of parent object


I have a zod schema like this:

export const memberSchema = z
  .object({
    name: z.string().min(3),
    lastname: z.string().min(3),
    nationality: zCountries,
    birthdate: z.date(),
    email: z.string().email(),
    tutor: z
      .object({
        name: z.string(),
        lastname: z.string(),
        documentType: zDocumentationType,
        documentValue: z.string(),
        email: z.string().email(),
        phone: z.string().regex(/^\d{9}$/)
      })
  }).refine((data) => .....)

Is it possible to validate tutor object is required only if birthdate is under 18 ? Because if member is +18 this fields are not needed.

I know how to validate a field depending of another one in refine function, but I don't know how to validate and entire object...

EXAMPLE:

  • { birthdate: '20/02/2015'} -> ALL Fields in tutor has to be filled and passing zod tutor object validation
  • {birthdate: '09/05/1988' } -> Tutor object is undefined and returns true.

Solution

  • After many tries I got what I want with z.union between object and undefined and super refine.

    Here is my code. Hope it helps someone in future.

    export const memberSchema = z
      .object({
        name: z.string().min(3),
        lastname: z.string().min(3),
        nationality: zCountries,
        birthdate: z.date(),
        email: z.string().email(),
        tutor: z.union([
          z.object({
            name: zTutorString,
            lastname: zTutorString,
            document: zTutorString,
            email: zTutorString.email(),
            phone: zTutorString.regex(/^\d{9}$/)
          }),
          z.undefined()
        ])
      })
      .superRefine((data, ctx) => {
        const validation =
          data.birthdate.getFullYear() + 18 < new Date().getFullYear() ||
          (data.birthdate.getFullYear() + 18 > new Date().getFullYear() && data.tutor !== undefined)
        if (!validation) {
          tutorFields.forEach((field) => {
            if (!data.tutor || (data.tutor && !data.tutor[field as keyof typeof data.tutor])) {
              ctx.addIssue({
                code: z.ZodIssueCode.custom,
                message: i18n.t('validation.required_tutor'),
                path: ['tutor', field]
              })
            }
          })
        }
      })