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

Coerce e Preprocess

z.coerce

z.coerce converte automaticamente il valore di input al tipo desiderato prima di validarlo. Usa i costruttori nativi JavaScript (String(), Number(), Boolean(), ecc.).

z.coerce.string()

Converte qualsiasi valore in stringa usando String():

import { z } from "zod";

const schema = z.coerce.string();
schema.parse(42);        // => "42"
schema.parse(true);      // => "true"
schema.parse(null);      // => "null"
schema.parse(undefined); // => "undefined"

z.coerce.number()

Converte il valore usando Number():

const schema = z.coerce.number();
schema.parse("42");     // => 42
schema.parse("3.14");   // => 3.14
schema.parse("");       // => 0
schema.parse(true);     // => 1
schema.parse(false);    // => 0
schema.parse("abc");    // => NaN (attenzione!)

Per evitare NaN, combina con altre validazioni:

const sicuro = z.coerce.number().finite();
sicuro.parse("42");   // => 42
sicuro.parse("abc");  // Errore: NaN non e' finite

z.coerce.boolean()

Converte usando Boolean():

const schema = z.coerce.boolean();
schema.parse("true");   // => true
schema.parse("false");  // => true (attenzione! "false" e' truthy)
schema.parse("");        // => false
schema.parse(0);         // => false
schema.parse(1);         // => true
schema.parse(null);      // => false

Attenzione: Boolean("false") restituisce true perche’ qualsiasi stringa non vuota e’ truthy. Per gestire i form HTML, usa z.preprocess().

z.coerce.date()

Converte usando new Date():

const schema = z.coerce.date();
schema.parse("2026-02-10");               // => Date
schema.parse(1707523200000);               // => Date (da timestamp)
schema.parse("10 Febbraio 2026");          // => Date
schema.parse("non-una-data");              // Errore: Invalid Date

Puoi combinare con validazioni di data:

const dataNascita = z.coerce.date()
  .min(new Date("1900-01-01"), "Data troppo indietro")
  .max(new Date(), "La data non puo' essere nel futuro");

z.coerce.bigint()

const schema = z.coerce.bigint();
schema.parse("42"); // => 42n
schema.parse(42);   // => 42n

z.preprocess()

z.preprocess() applica una funzione di trasformazione prima della validazione. Ti da’ controllo completo sulla conversione.

const stringToNumber = z.preprocess(
  (val) => {
    if (typeof val === "string") return parseInt(val, 10);
    return val;
  },
  z.number().int().positive()
);

stringToNumber.parse("42");   // => 42
stringToNumber.parse(42);     // => 42
stringToNumber.parse("abc");  // Errore (NaN non e' int/positive)

Gestire i Booleani dai Form HTML

Nei form HTML, i checkbox inviano "on" o non inviano nulla. Con z.preprocess() puoi gestirlo:

const checkboxSchema = z.preprocess(
  (val) => {
    if (typeof val === "string") {
      return val === "true" || val === "on" || val === "1" || val === "si";
    }
    return Boolean(val);
  },
  z.boolean()
);

checkboxSchema.parse("on");     // => true
checkboxSchema.parse("true");   // => true
checkboxSchema.parse("false");  // => false
checkboxSchema.parse("1");      // => true
checkboxSchema.parse(undefined); // => false

Differenza tra coerce e preprocess

Aspetto z.coerce z.preprocess()
Controllo Usa i costruttori JS nativi Funzione custom
Flessibilita’ Limitata Totale
Semplicita’ Molto semplice Richiede piu’ codice
Gestione edge case No Si

Usa z.coerce per conversioni semplici e standard. Usa z.preprocess() quando hai bisogno di logica personalizzata.

Caso d’Uso: Query Parameters

I query params di un URL sono sempre stringhe. Ecco come validarli:

const QueryParams = z.object({
  pagina: z.coerce.number().int().positive().default(1),
  limite: z.coerce.number().int().min(1).max(100).default(20),
  ordine: z.enum(["asc", "desc"]).default("desc"),
  cerca: z.string().optional(),
  attivo: z.preprocess(
    (val) => val === "true" || val === "1",
    z.boolean().default(true)
  ),
});

// Simula i query params: ?pagina=2&limite=50&attivo=true
const params = QueryParams.parse({
  pagina: "2",
  limite: "50",
  attivo: "true",
});
// => { pagina: 2, limite: 50, ordine: "desc", cerca: undefined, attivo: true }

Caso d’Uso: Form HTML

I form HTML inviano tutti i dati come stringhe. Ecco uno schema per gestirlo:

const FormContatto = z.object({
  nome: z.string().min(1, "Nome obbligatorio").trim(),
  email: z.string().email("Email non valida"),
  eta: z.coerce.number({
    invalid_type_error: "L'eta' deve essere un numero",
  }).int().min(18, "Devi avere almeno 18 anni"),
  dataNascita: z.coerce.date(),
  budget: z.preprocess(
    (val) => {
      if (typeof val === "string") {
        // Rimuovi simbolo valuta e separatore migliaia
        return parseFloat(val.replace(/[€\s.]/g, "").replace(",", "."));
      }
      return val;
    },
    z.number().positive("Il budget deve essere positivo")
  ),
  accettaTermini: z.preprocess(
    (val) => val === "on" || val === "true" || val === true,
    z.literal(true, {
      errorMap: () => ({ message: "Devi accettare i termini" }),
    })
  ),
});

// Dati dal form HTML
FormContatto.parse({
  nome: " Marco Rossi ",
  email: "marco@esempio.it",
  eta: "28",
  dataNascita: "1998-05-15",
  budget: "1.500,00",
  accettaTermini: "on",
});

Caso d’Uso: Environment Variables

Le variabili d’ambiente sono sempre stringhe:

const EnvSchema = z.object({
  NODE_ENV: z.enum(["development", "production", "test"]),
  PORT: z.coerce.number().int().default(3000),
  DATABASE_URL: z.string().url(),
  JWT_SECRET: z.string().min(32),
  ENABLE_CACHE: z.preprocess(
    (val) => val === "true" || val === "1",
    z.boolean().default(false)
  ),
  MAX_UPLOAD_SIZE: z.coerce.number().positive().default(10485760),
});

const env = EnvSchema.parse(process.env);