Convertissez vos interfaces TypeScript en schémas Zod instantanément. Génération automatique du code de validation avec types inférés, optionnels, unions, imbriqués et Record.
Convertisseur Interface TypeScript → Zod Schema
Qu'est-ce que Zod et pourquoi l'adopter
Pour qui : Développeurs TypeScript, équipes Angular / React, architectes frontend, ingénieurs backend Node.js.
Zod est une bibliothèque de validation de schémas TypeScript-first. Contrairement à Yup ou Joi, Zod infère automatiquement le type TypeScript depuis le schéma — vous n'écrivez plus jamais deux fois la même structure.
- ✔ Zero dépendances — 8 Ko minifié+gzippé
- ✔ TypeScript natif —
z.infer<typeof MySchema>remplace votre interface - ✔ Validation runtime — Validez les données à la frontière (API, formulaire, localStorage)
- ✔ Messages d'erreur personnalisables — Retours utilisateur précis sans effort
- ✔ Composable —
.optional(),.nullable(),.refine(),.transform()
Pourquoi ne pas garder uniquement les interfaces TypeScript ?
Les types TypeScript sont effacés à la compilation — ils n'existent pas à l'exécution. Une réponse API non conforme passera votre compilateur mais plantera votre application. Zod résout ce problème :
// Sans Zod — le compilateur est aveugle aux erreurs runtime
const user: User = await fetch('/api/user').then(r => r.json());
// user.age pourrait être une string, null, ou absent → crash silencieux
// Avec Zod — validation stricte à la frontière
const user = UserSchema.parse(await fetch('/api/user').then(r => r.json()));
// Si l'API retourne un champ invalide → ZodError immédiat, lisible
Comparaison Zod vs alternatives
| Bibliothèque | TypeScript natif | Inférence de type | Taille | Popularité (2025) |
|---|---|---|---|---|
| Zod | ✔ Oui | ✔ Automatique | 8 Ko | ⭐ 35 M dl/sem |
| Yup | Partiel | Manuelle | 14 Ko | 18 M dl/sem |
| Joi | Via @types | Manuelle | 45 Ko | 12 M dl/sem |
| class-validator | Décorateurs | Manuelle | 20 Ko | 8 M dl/sem |
Comment utiliser ce convertisseur
Le convertisseur analyse votre interface TypeScript et génère le schéma Zod correspondant avec les bonnes méthodes de validation.
- Collez votre interface dans la zone de gauche (ou cliquez sur un exemple)
- Ajustez les options :
export const: Préfixeexportsur chaque schémaz.infer<>: Génère le type inférétype User = z.infer<typeof UserSchema>.strict(): Rejette les clés inconnues (recommandé pour les APIs)- Commentaires : Ajoute le type TypeScript original en commentaire
- Cliquez "Convertir" ou modifiez le texte (conversion auto après 30 caractères)
- Copiez le résultat et collez-le dans votre projet
Interfaces multiples et imbriquées
Si vous collez plusieurs interfaces à la fois, chaque interface génère son propre schéma. Les références entre interfaces sont détectées automatiquement :
// Entrée TypeScript
export interface Address {
street: string;
city: string;
}
export interface User {
id: number;
address: Address; // ← référence détectée
}
// Schéma Zod généré
export const AddressSchema = z.object({
street: z.string(),
city: z.string(),
});
export type Address = z.infer<typeof AddressSchema>;
export const UserSchema = z.object({
id: z.number(),
address: AddressSchema, // ← référence résolue
});
export type User = z.infer<typeof UserSchema>;
Mapping des types TypeScript vers Zod
Le convertisseur gère tous les types courants de TypeScript. Voici la table de correspondance complète :
| TypeScript | Zod généré | Notes |
|---|---|---|
string | z.string() | Texte quelconque |
number | z.number() | Entier ou float |
boolean | z.boolean() | true / false |
Date | z.date() | Objet Date JS |
any | z.any() | Pas de validation |
unknown | z.unknown() | Type inconnu sûr |
null | z.null() | Valeur null explicite |
undefined | z.undefined() | Valeur undefined |
never | z.never() | Type impossible |
string[] | z.array(z.string()) | Tableau de strings |
Array<number> | z.array(z.number()) | Syntaxe générique |
string | null | z.string().nullable() | Union avec null |
number? (champ optionnel) | z.number().optional() | Optionnel via ? |
string | number | z.union([z.string(), z.number()]) | Union arbitraire |
Record<string, T> | z.record(z.string(), z.T()) | Dictionnaire |
[string, number] | z.tuple([z.string(), z.number()]) | Tuple typé |
'admin' | 'user' | z.union([z.literal('admin'), z.literal('user')]) | Union de littéraux |
Partial<T> | TSchema.partial() | Tous champs optionnels |
Promise<T> | z.promise(TSchema) | Promise validée |
A & B | z.intersection(ASchema, BSchema) | Intersection |
Gestion des énumérations TypeScript
Les enum TypeScript ne sont pas des interfaces — utilisez z.enum() ou z.nativeEnum() manuellement :
// TypeScript enum
enum Role { Admin = 'ADMIN', User = 'USER', Guest = 'GUEST' }
// Option 1 : z.enum (valeurs string uniquement)
const RoleSchema = z.enum(['ADMIN', 'USER', 'GUEST']);
type Role = z.infer<typeof RoleSchema>; // 'ADMIN' | 'USER' | 'GUEST'
// Option 2 : z.nativeEnum (enum TypeScript natif)
const RoleSchema = z.nativeEnum(Role);
type RoleType = z.infer<typeof RoleSchema>; // Role enum
Validation avancée : refinements et transforms
Après conversion, vous pouvez enrichir votre schéma Zod avec des validations métier que TypeScript ne peut pas exprimer nativement.
Refinements — valider au-delà des types
// Schéma généré par le convertisseur
export const UserSchema = z.object({
id: z.number(),
email: z.string(),
age: z.number().optional(),
});
// Enrichissement manuel après conversion
export const UserSchema = z.object({
id: z.number().int().positive(), // entier positif
email: z.string().email(), // format email validé
age: z.number().min(0).max(150).optional(), // plage de valeurs
username: z.string().min(3).max(20)
.regex(/^[a-z0-9_]+$/, 'Lettres minuscules, chiffres et _ uniquement'),
password: z.string().min(8).refine(
(pw) => /[A-Z]/.test(pw),
'Le mot de passe doit contenir au moins une majuscule'
),
});
Transforms — normaliser les données à la validation
// Trim et lowercase automatiques
const EmailSchema = z.string()
.trim()
.toLowerCase()
.email();
// Convertir string ISO en Date
const DateStringSchema = z.string()
.datetime()
.transform((val) => new Date(val));
// Coerce — forcer la conversion de type
const IdSchema = z.coerce.number(); // accepte "42" → 42
Validation conditionnelle avec superRefine
const CheckoutSchema = z.object({
paymentMethod: z.enum(['card', 'paypal', 'bank']),
cardNumber: z.string().optional(),
iban: z.string().optional(),
}).superRefine((data, ctx) => {
if (data.paymentMethod === 'card' && !data.cardNumber) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Le numéro de carte est requis',
path: ['cardNumber'],
});
}
if (data.paymentMethod === 'bank' && !data.iban) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "L'IBAN est requis pour un virement",
path: ['iban'],
});
}
});
parse vs safeParse
// parse — lève ZodError si invalide
try {
const user = UserSchema.parse(rawData);
} catch (e) {
if (e instanceof z.ZodError) console.log(e.errors);
}
// safeParse — retourne { success, data } ou { success, error }
const result = UserSchema.safeParse(rawData);
if (!result.success) {
// result.error.errors → tableau d'erreurs avec path et message
result.error.errors.forEach(err => console.log(err.path, err.message));
} else {
// result.data est typé correctement
processUser(result.data);
}
Intégration React Hook Form et Angular
React Hook Form + Zod (zodResolver)
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
// 1. Définir le schéma (généré par notre outil)
const LoginSchema = z.object({
email: z.string().email('Email invalide'),
password: z.string().min(8, '8 caractères minimum'),
});
type LoginForm = z.infer<typeof LoginSchema>;
// 2. Utiliser dans le formulaire
function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm<LoginForm>({
resolver: zodResolver(LoginSchema),
});
const onSubmit = (data: LoginForm) => {
// data est typé et validé
console.log(data.email, data.password);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} />
{errors.email && <p>{errors.email.message}</p>}
<input type="password" {...register('password')} />
{errors.password && <p>{errors.password.message}</p>}
<button type="submit">Connexion</button>
</form>
);
}
Angular Reactive Forms + Zod
// schema.ts (généré par le convertisseur)
import { z } from 'zod';
export const UserFormSchema = z.object({
name: z.string().min(2).max(50),
email: z.string().email(),
age: z.number().min(18).optional(),
});
export type UserFormData = z.infer<typeof UserFormSchema>;
// user-form.component.ts
import { Component } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { UserFormSchema, UserFormData } from './schema';
@Component({
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<input formControlName="name" />
<input formControlName="email" />
<button type="submit">Envoyer</button>
</form>
`
})
export class UserFormComponent {
form = this.fb.group({
name: [''],
email: [''],
age: [null as number | null],
});
constructor(private fb: FormBuilder) {}
onSubmit() {
const result = UserFormSchema.safeParse(this.form.value);
if (!result.success) {
// Afficher les erreurs Zod dans le template
result.error.errors.forEach(e => console.error(e.path, e.message));
return;
}
// result.data est typé UserFormData
this.submitToApi(result.data);
}
private submitToApi(data: UserFormData) {
// data.name, data.email sont validés et typés
}
}
Validation de réponse API dans Angular HttpClient
// api.service.ts
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map, catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
import { UserSchema } from './schema';
@Injectable({ providedIn: 'root' })
export class ApiService {
private http = inject(HttpClient);
getUser(id: number) {
return this.http.get(`/api/users/${id}`).pipe(
map(raw => UserSchema.parse(raw)), // valide + type au runtime
catchError(err => {
if (err.name === 'ZodError') {
console.error('Réponse API invalide:', err.errors);
}
return throwError(() => err);
})
);
}
}
FAQ — Questions fréquentes
Le convertisseur gère-t-il les génériques TypeScript comme Response<T> ?
Les paramètres de type génériques (<T>) sont ignorés à la conversion car Zod utilise une approche compositionnelle. Créez un helper :
// Au lieu de Response<T> TypeScript :
const responseSchema = <T extends z.ZodTypeAny>(dataSchema: T) =>
z.object({
data: dataSchema,
status: z.number(),
message: z.string().optional(),
});
// Utilisation :
const UserResponseSchema = responseSchema(UserSchema);
type UserResponse = z.infer<typeof UserResponseSchema>;
Comment installer Zod dans mon projet ?
npm install zod
# ou
yarn add zod
# ou
pnpm add zod
Zod est compatible Node.js 12+, navigateurs modernes, Deno, et Bun. Aucune configuration supplémentaire requise.
Quelle est la différence entre .nullable() et .optional() ?
| Méthode | Valeurs acceptées | TypeScript équivalent |
|---|---|---|
z.string() | string uniquement | string |
z.string().nullable() | string ou null | string | null |
z.string().optional() | string ou undefined | string | undefined (champ optionnel) |
z.string().nullish() | string, null ou undefined | string | null | undefined |
Peut-on utiliser Zod côté serveur Node.js / NestJS ?
Oui. Dans NestJS, la bibliothèque nestjs-zod intègre Zod avec les DTOs et les pipes de validation. Exemple :
import { createZodDto } from 'nestjs-zod';
import { z } from 'zod';
const CreateUserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
role: z.enum(['admin', 'user']).default('user'),
});
export class CreateUserDto extends createZodDto(CreateUserSchema) {}
// Dans le controller
@Post()
create(@Body() dto: CreateUserDto) {
// dto est automatiquement validé et typé
}
Comment afficher les erreurs Zod en français ?
import { z } from 'zod';
import { zodI18nMap } from 'zod-i18n-map';
import translation from 'zod-i18n-map/locales/fr/zod.json';
import i18next from 'i18next';
await i18next.init({ lng: 'fr', resources: { fr: { zod: translation } } });
z.setErrorMap(zodI18nMap);
// Maintenant les erreurs Zod sont en français
const result = z.string().email().safeParse('invalide');
// result.error.errors[0].message → "E-mail invalide"
Zod v3 vs v4 — quelles différences ?
Zod v4 (sorti en 2025) apporte des améliorations majeures de performance (5× plus rapide sur les schemas complexes), une meilleure gestion des messages d'erreur, et le support de z.object().merge() plus robuste. Ce convertisseur génère du code compatible avec Zod v3 et v4.
Conclusion
Convertir vos interfaces TypeScript en schémas Zod est l'une des meilleures décisions pour la qualité de votre code frontend. Vous obtenez :
- Validation runtime à la frontière (API, formulaires, localStorage)
- Types TypeScript inférés automatiquement — plus de duplication
- Messages d'erreur précis et personnalisables
- Compatibilité universelle (Angular, React, Vue, Node.js, Deno)
- Transformations et normalisations intégrées au schéma
Utilisez ce convertisseur comme point de départ, puis enrichissez vos schémas avec .min(), .max(), .email(), .refine() pour des validations métier précises. Votre code sera plus robuste, plus maintenable et vos bugs de production seront détectés avant même d'atteindre l'utilisateur.