Search code examples
react-hook-formzod

Zod and conditional validation


I have a form which use React Hook Form and Zod as validator.

I have to validate some fields if the Committente type is Privato and some others if is Azienda (Let's just ignore Azienda Estera)

For Privato type I have those fields:

  • Nome
  • Cognome
  • Indirizzo
  • CAP
  • Città
  • Prov
  • Stato
  • Codice Fiscale
  • Email
  • Telefono

while for Azienda those others:

  • Nome Azienda
  • Indirizzo
  • CAP
  • Città
  • Prov
  • Stato
  • Partita IVA
  • Email
  • Telefono

So, basically, the fields Nome, Cognome, and Codice Fiscale are undefined when azienda is selected and the fields Nome Azienda and Partita Iva are undefined when privato is selected.

How can I build this type of schema?

I tried something like this:

const mySchema = z.union([
        z.object({
            tipologia: z.literal("privato"),
            nome: z.string().min(1, { message: "Il nome è richiesto" }),
            cognome: z.string().min(1, { message: "Il cognome è richiesto" }),
            ...
        }),
        z.object({
            tipologia: z.literal("azienda"),
            nomeAzienda: z.string().min(5, { message: "Il nome è richiesto" }),
            ...
        }),
    ])

but didn't work.

Committente private type

enter image description here


Solution

  • I made it works using discriminatedUnion method. Actually, while testing, I was just forgetting to implement the invalid and error message on the fields, so I didn't have any feedbacks and I thought it doesn't work.

    
    // Base Schema
    const baseSchema = z.object({
        indirizzo: z.string().min(3, { message: "L'indirizzo è richiesto" }),
        cap: z
            .string()
            ...
    })
    
    // Schema
    const mySchema = z.discriminatedUnion("tipologia", [
            z
                .object({
                    tipologia: z.literal("privato"),
                    nome: z.string().min(1, { message: "Il nome è richiesto" }),
                    cognome: z.string().min(1, { message: "Il cognome è richiesto" }),
                    cf: z.string().min(16, { message: "Il Codice Fiscale è richiesto" }),
                    stato: z.literal("Italia"),
                })
                .merge(baseSchema),
            z
                .object({
                    tipologia: z.literal("azienda"),
                    nomeAzienda: z.string().min(1, { message: "Il nome è richiesto" }),
                    piva: z.string().min(11, { message: "La Partita IVA è richiesta" }),
                    stato: z.literal("Italia"),
                })
                .merge(baseSchema),
            z
                .object({
                    tipologia: z.literal("aziendaEstera"),
                    nomeAzienda: z.string().min(1, { message: "Il nome è richiesto" }),
                    piva: z.string().min(11, { message: "La Partita IVA è richiesta" }),
                    stato: z.string().min(1, { message: "Indicare lo Stato" }),
                })
                .merge(baseSchema),
        ]),