00
:
00
:
00
:
00
Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Union, Enum e Literal

z.literal()

Valida un valore esatto e immutabile:

import { z } from "zod";

const cinque = z.literal(5);
cinque.parse(5);  // OK
cinque.parse(6);  // Errore

const ciao = z.literal("ciao");
ciao.parse("ciao");   // OK
ciao.parse("mondo");  // Errore

const vero = z.literal(true);
vero.parse(true);  // OK
vero.parse(false); // Errore

z.enum()

Crea un enum Zod da un array di stringhe. Il tipo risultante e’ una union di stringhe letterali.

const Ruolo = z.enum(["admin", "utente", "moderatore"]);

Ruolo.parse("admin");   // OK
Ruolo.parse("ospite");  // Errore

type Ruolo = z.infer<typeof Ruolo>;
// "admin" | "utente" | "moderatore"

Accedere ai Valori

Ruolo.options;  // ["admin", "utente", "moderatore"]
Ruolo.enum;     // { admin: "admin", utente: "utente", moderatore: "moderatore" }

// Utile per accedere in modo type-safe
Ruolo.enum.admin; // "admin"

Escludere o Estrarre Valori

const SoloAdmin = Ruolo.extract(["admin", "moderatore"]);
type SoloAdmin = z.infer<typeof SoloAdmin>;
// "admin" | "moderatore"

const SenzaAdmin = Ruolo.exclude(["admin"]);
type SenzaAdmin = z.infer<typeof SenzaAdmin>;
// "utente" | "moderatore"

z.nativeEnum()

Se hai gia’ un enum TypeScript, puoi usarlo direttamente:

enum Colore {
  Rosso = "rosso",
  Verde = "verde",
  Blu = "blu",
}

const ColoreSchema = z.nativeEnum(Colore);
ColoreSchema.parse(Colore.Rosso); // OK
ColoreSchema.parse("rosso");      // OK
ColoreSchema.parse("giallo");     // Errore

type ColoreType = z.infer<typeof ColoreSchema>;
// Colore

Funziona anche con enum numerici:

enum Stato {
  Attivo = 0,
  Inattivo = 1,
  Sospeso = 2,
}

const StatoSchema = z.nativeEnum(Stato);
StatoSchema.parse(0); // OK
StatoSchema.parse(3); // Errore

z.union()

Crea un tipo unione: il dato deve corrispondere ad almeno uno degli schemi.

const StringaONumero = z.union([z.string(), z.number()]);

StringaONumero.parse("ciao"); // OK
StringaONumero.parse(42);     // OK
StringaONumero.parse(true);   // Errore

Union di Oggetti

const Risultato = z.union([
  z.object({ success: z.literal(true), data: z.string() }),
  z.object({ success: z.literal(false), errore: z.string() }),
]);

Risultato.parse({ success: true, data: "ok" });          // OK
Risultato.parse({ success: false, errore: "fallito" });   // OK

z.discriminatedUnion()

Piu’ efficiente di z.union() quando gli oggetti hanno un campo discriminante comune:

const Evento = z.discriminatedUnion("tipo", [
  z.object({
    tipo: z.literal("click"),
    x: z.number(),
    y: z.number(),
  }),
  z.object({
    tipo: z.literal("keypress"),
    tasto: z.string(),
  }),
  z.object({
    tipo: z.literal("scroll"),
    direzione: z.enum(["su", "giu"]),
    distanza: z.number(),
  }),
]);

Evento.parse({ tipo: "click", x: 100, y: 200 });       // OK
Evento.parse({ tipo: "keypress", tasto: "Enter" });      // OK
Evento.parse({ tipo: "scroll", direzione: "su", distanza: 50 }); // OK

type Evento = z.infer<typeof Evento>;

Il vantaggio e’ che Zod guarda prima il campo tipo e poi valida solo lo schema corrispondente, generando messaggi di errore piu’ precisi.

z.intersection()

Combina due schemi: il dato deve soddisfare entrambi:

const ConNome = z.object({ nome: z.string() });
const ConEta = z.object({ eta: z.number() });

const Persona = z.intersection(ConNome, ConEta);
Persona.parse({ nome: "Marco", eta: 28 }); // OK
Persona.parse({ nome: "Marco" });            // Errore: manca eta

type Persona = z.infer<typeof Persona>;
// { nome: string } & { eta: number }

Nella maggior parte dei casi, .merge() e’ preferibile a z.intersection() per gli oggetti.

Optional e Nullable

.optional()

Rende il campo opzionale (undefined e’ accettato):

const schema = z.object({
  nome: z.string(),
  bio: z.string().optional(), // string | undefined
});

schema.parse({ nome: "Marco" });              // OK
schema.parse({ nome: "Marco", bio: undefined }); // OK
schema.parse({ nome: "Marco", bio: "Dev" });  // OK

.nullable()

Accetta anche null:

const campo = z.string().nullable();
campo.parse("ciao"); // OK
campo.parse(null);    // OK
campo.parse(undefined); // Errore

.nullish()

Combina optional e nullable:

const campo = z.string().nullish();
campo.parse("ciao");     // OK
campo.parse(null);       // OK
campo.parse(undefined);  // OK

type Campo = z.infer<typeof campo>;
// string | null | undefined

.unwrap()

Per recuperare lo schema interno da un optional o nullable:

const opzionale = z.string().optional();
const interno = opzionale.unwrap();
// => z.string()

Esempio Pratico: Sistema di Notifiche

const Notifica = z.discriminatedUnion("canale", [
  z.object({
    canale: z.literal("email"),
    destinatario: z.string().email(),
    oggetto: z.string(),
    corpo: z.string(),
  }),
  z.object({
    canale: z.literal("sms"),
    numero: z.string().regex(/^\+\d{10,15}$/),
    messaggio: z.string().max(160),
  }),
  z.object({
    canale: z.literal("push"),
    deviceToken: z.string(),
    titolo: z.string(),
    corpo: z.string().optional(),
  }),
]);

type Notifica = z.infer<typeof Notifica>;