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

Messaggi di Errore Personalizzati

Messaggi di Errore Inline

Il modo piu’ semplice per personalizzare i messaggi e’ passarli direttamente ai metodi di validazione:

import { z } from "zod";

const schema = z.object({
  nome: z.string({
    required_error: "Il nome e' obbligatorio",
    invalid_type_error: "Il nome deve essere una stringa",
  }).min(2, { message: "Il nome deve avere almeno 2 caratteri" }),

  eta: z.number({
    required_error: "L'eta' e' obbligatoria",
    invalid_type_error: "L'eta' deve essere un numero",
  }).int("L'eta' deve essere un numero intero")
    .min(0, "L'eta' non puo' essere negativa"),

  email: z.string()
    .min(1, "L'email e' obbligatoria")
    .email("Formato email non valido"),
});

ZodError

Quando la validazione fallisce, Zod genera un oggetto ZodError che contiene un array issues:

const result = schema.safeParse({ nome: "", eta: -5, email: "abc" });

if (!result.success) {
  console.log(result.error.issues);
  // [
  //   { code: "too_small", minimum: 2, message: "Il nome deve avere...", path: ["nome"] },
  //   { code: "too_small", minimum: 0, message: "L'eta' non puo'...", path: ["eta"] },
  //   { code: "invalid_string", message: "Formato email...", path: ["email"] },
  // ]
}

Ogni issue ha queste proprieta’ principali:

  • code: il tipo di errore (es. too_small, invalid_type, custom)
  • message: il messaggio di errore
  • path: un array che indica il percorso del campo (es. ["indirizzo", "citta"])

.format()

Trasforma gli errori in una struttura ad albero che rispecchia la forma dell’oggetto:

const UtenteSchema = z.object({
  nome: z.string().min(1),
  indirizzo: z.object({
    via: z.string().min(1),
    cap: z.string().length(5),
  }),
});

const result = UtenteSchema.safeParse({
  nome: "",
  indirizzo: { via: "", cap: "123" },
});

if (!result.success) {
  const formatted = result.error.format();

  console.log(formatted.nome?._errors);
  // ["String must contain at least 1 character(s)"]

  console.log(formatted.indirizzo?.via?._errors);
  // ["String must contain at least 1 character(s)"]

  console.log(formatted.indirizzo?.cap?._errors);
  // ["String must contain exactly 5 character(s)"]
}

.flatten()

Produce una struttura piatta, ideale per i form:

if (!result.success) {
  const flat = result.error.flatten();

  console.log(flat.fieldErrors);
  // {
  //   nome: ["String must contain at least 1 character(s)"],
  //   "indirizzo.via": [...],
  //   "indirizzo.cap": [...],
  // }

  console.log(flat.formErrors);
  // [] -- errori non associati a un campo specifico
}

flatten con Mapper Custom

const flat = result.error.flatten((issue) => ({
  messaggio: issue.message,
  codice: issue.code,
}));

Error Map Globale

Puoi personalizzare i messaggi di errore a livello globale:

const errorMapItaliano: z.ZodErrorMap = (issue, ctx) => {
  switch (issue.code) {
    case z.ZodIssueCode.invalid_type:
      if (issue.expected === "string") {
        return { message: "Questo campo deve essere una stringa" };
      }
      if (issue.expected === "number") {
        return { message: "Questo campo deve essere un numero" };
      }
      return { message: `Tipo non valido: atteso ${issue.expected}, ricevuto ${issue.received}` };

    case z.ZodIssueCode.too_small:
      if (issue.type === "string") {
        return { message: `Deve contenere almeno ${issue.minimum} caratteri` };
      }
      if (issue.type === "number") {
        return { message: `Il valore deve essere almeno ${issue.minimum}` };
      }
      return { message: `Troppo piccolo` };

    case z.ZodIssueCode.too_big:
      if (issue.type === "string") {
        return { message: `Deve contenere al massimo ${issue.maximum} caratteri` };
      }
      return { message: `Troppo grande` };

    case z.ZodIssueCode.invalid_string:
      if (issue.validation === "email") {
        return { message: "Email non valida" };
      }
      if (issue.validation === "url") {
        return { message: "URL non valido" };
      }
      return { message: "Stringa non valida" };

    default:
      return { message: ctx.defaultError };
  }
};

// Imposta la error map globalmente
z.setErrorMap(errorMapItaliano);

Dopo averla impostata, tutti gli schemi useranno questi messaggi.

Error Map per Schema

Puoi anche impostare una error map su un singolo schema:

const eta = z.number({
  errorMap: (issue, ctx) => {
    if (issue.code === z.ZodIssueCode.invalid_type) {
      return { message: "L'eta' deve essere un numero" };
    }
    return { message: ctx.defaultError };
  },
}).int().min(0).max(150);

Gestione User-Friendly degli Errori

Un pattern utile per mostrare errori nei form:

function validaForm<T extends z.ZodTypeAny>(
  schema: T,
  dati: unknown
): { successo: true; dati: z.infer<T> } | { successo: false; errori: Record<string, string[]> } {
  const result = schema.safeParse(dati);

  if (result.success) {
    return { successo: true, dati: result.data };
  }

  const errori: Record<string, string[]> = {};
  for (const issue of result.error.issues) {
    const campo = issue.path.join(".");
    if (!errori[campo]) {
      errori[campo] = [];
    }
    errori[campo].push(issue.message);
  }

  return { successo: false, errori };
}

// Uso
const risultato = validaForm(schema, { nome: "", eta: -1 });
if (!risultato.successo) {
  console.log(risultato.errori);
  // { nome: ["Il nome deve avere almeno 2 caratteri"], eta: ["L'eta' non puo'..."] }
}

i18n (Internazionalizzazione)

Per supportare piu’ lingue, puoi creare una error map per ogni lingua e cambiarla dinamicamente:

const errorMaps: Record<string, z.ZodErrorMap> = {
  it: (issue, ctx) => {
    if (issue.code === z.ZodIssueCode.too_small && issue.type === "string") {
      return { message: `Minimo ${issue.minimum} caratteri` };
    }
    return { message: ctx.defaultError };
  },
  en: (issue, ctx) => {
    if (issue.code === z.ZodIssueCode.too_small && issue.type === "string") {
      return { message: `Minimum ${issue.minimum} characters` };
    }
    return { message: ctx.defaultError };
  },
};

function setLingua(lingua: "it" | "en") {
  z.setErrorMap(errorMaps[lingua]);
}

setLingua("it");