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

Oggetti e Nested

z.object() in Dettaglio

z.object() è il cuore di Zod. Definisce la struttura di un oggetto con chiavi e tipi specifici.

import { z } from "zod";

const Utente = z.object({
  id: z.number(),
  nome: z.string(),
  email: z.string().email(),
  eta: z.number().optional(),
});

type Utente = z.infer<typeof Utente>;
// { id: number; nome: string; email: string; eta?: number }

Per default, Zod ignora (strip) le proprietà extra non dichiarate nello schema.

Oggetti Nested

Puoi annidare z.object() all’interno di altri schemi:

const Indirizzo = z.object({
  via: z.string(),
  citta: z.string(),
  cap: z.string().length(5),
  provincia: z.string().length(2).toUpperCase(),
});

const UtenteCompleto = z.object({
  nome: z.string(),
  email: z.string().email(),
  indirizzo: Indirizzo,
  contatti: z.object({
    telefono: z.string().optional(),
    cellulare: z.string(),
  }),
});

type UtenteCompleto = z.infer<typeof UtenteCompleto>;

.pick() e .omit()

Come i tipi utility di TypeScript Pick e Omit:

const Utente = z.object({
  id: z.number(),
  nome: z.string(),
  email: z.string().email(),
  password: z.string(),
});

// Solo nome ed email
const UtentePublico = Utente.pick({ nome: true, email: true });
type UtentePublico = z.infer<typeof UtentePublico>;
// { nome: string; email: string }

// Tutto tranne password
const UtenteSenzaPassword = Utente.omit({ password: true });
type UtenteSenzaPassword = z.infer<typeof UtenteSenzaPassword>;
// { id: number; nome: string; email: string }

.partial() e .required()

.partial() rende tutti i campi opzionali (come Partial<T> in TypeScript):

const Utente = z.object({
  nome: z.string(),
  email: z.string(),
  eta: z.number(),
});

const UtentePartial = Utente.partial();
type UtentePartial = z.infer<typeof UtentePartial>;
// { nome?: string; email?: string; eta?: number }

Puoi rendere opzionali solo campi specifici:

const AggiornamentoUtente = Utente.partial({ email: true, eta: true });
type AggiornamentoUtente = z.infer<typeof AggiornamentoUtente>;
// { nome: string; email?: string; eta?: number }

.required() fa l’opposto, rendendo tutti i campi obbligatori:

const UtenteRequired = UtentePartial.required();
// Tutti i campi tornano obbligatori

.extend() e .merge()

.extend()

Aggiunge nuovi campi a uno schema esistente:

const Base = z.object({
  nome: z.string(),
  email: z.string(),
});

const ConRuolo = Base.extend({
  ruolo: z.enum(["admin", "utente"]),
  createdAt: z.date(),
});

type ConRuolo = z.infer<typeof ConRuolo>;
// { nome: string; email: string; ruolo: "admin" | "utente"; createdAt: Date }

.extend() può anche sovrascrivere campi esistenti:

const Sovrascritto = Base.extend({
  email: z.string().email().optional(), // ora è opzionale
});

.merge()

Unisce due schemi oggetto. Simile a .extend() ma accetta un intero schema:

const InfoBase = z.object({ nome: z.string(), eta: z.number() });
const InfoContatto = z.object({ email: z.string(), telefono: z.string() });

const InfoCompleta = InfoBase.merge(InfoContatto);
type InfoCompleta = z.infer<typeof InfoCompleta>;
// { nome: string; eta: number; email: string; telefono: string }

.passthrough(), .strict() e .strip()

Questi metodi controllano come Zod gestisce le proprietà non riconosciute.

.strip() (Default)

Rimuove le proprietà extra:

const schema = z.object({ nome: z.string() });
schema.parse({ nome: "Marco", extra: true });
// => { nome: "Marco" } -- "extra" viene rimossa

.passthrough()

Mantiene le proprietà extra nell’output:

const schema = z.object({ nome: z.string() }).passthrough();
schema.parse({ nome: "Marco", extra: true });
// => { nome: "Marco", extra: true }

.strict()

Lancia un errore se ci sono proprietà extra:

const schema = z.object({ nome: z.string() }).strict();

schema.parse({ nome: "Marco" });              // OK
schema.parse({ nome: "Marco", extra: true }); // Errore!
// ZodError: Unrecognized key(s) in object: 'extra'

.keyof()

Estrae le chiavi di uno schema come enum:

const Utente = z.object({
  nome: z.string(),
  email: z.string(),
  eta: z.number(),
});

const ChiaviUtente = Utente.keyof();
ChiaviUtente.parse("nome");  // OK
ChiaviUtente.parse("email"); // OK
ChiaviUtente.parse("altro"); // Errore

type Key = z.infer<typeof ChiaviUtente>;
// "nome" | "email" | "eta"

Esempio Pratico: CRUD

// Schema base
const Prodotto = z.object({
  id: z.number().int().positive(),
  nome: z.string().min(1),
  prezzo: z.number().positive(),
  categoria: z.string(),
  attivo: z.boolean().default(true),
});

// Per la creazione (senza id, viene generato dal DB)
const CreaProdotto = Prodotto.omit({ id: true });

// Per l'aggiornamento (tutti i campi opzionali tranne id)
const AggiornaProdotto = Prodotto.partial().required({ id: true });

// Per la risposta API (con timestamp aggiuntivi)
const ProdottoResponse = Prodotto.extend({
  createdAt: z.string().datetime(),
  updatedAt: z.string().datetime(),
});

type CreaProdotto = z.infer<typeof CreaProdotto>;
type AggiornaProdotto = z.infer<typeof AggiornaProdotto>;
type ProdottoResponse = z.infer<typeof ProdottoResponse>;