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 errorepath: 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");