
Arrêtez d'écrire des interfaces TypeScript à la main : utilisez un générateur JSON
📷 Roman Synkevych / PexelsArrêtez d'écrire des interfaces TypeScript à la main : utilisez un générateur JSON
Écrire manuellement des interfaces TypeScript à partir de payloads JSON est fastidieux et source d'erreurs. Voici comment l'automatiser et à quoi faire attention.
Si vous avez déjà passé vingt minutes à écrire à la main des interfaces TypeScript à partir d'un payload JSON reçu d'une API, vous savez déjà que c'est l'une de ces tâches qui semblent importantes mais qui sont presque entièrement mécaniques. Vous ne pensez pas. Vous copiez simplement des noms de champs et inférez des types. C'est le genre de travail qu'une machine devrait faire.
Et pourtant, beaucoup de développeurs le font encore à la main. Ce guide explique pourquoi cela vaut la peine d'arrêter, ce qu'un générateur JSON vers TypeScript produit réellement, et — plus important — ce qu'il ne peut pas faire pour que vous sachiez où prendre le relais.
Pourquoi les interfaces TypeScript comptent vraiment
Avant d'entrer dans l'outil, il vaut la peine d'être précis sur ce qu'on obtient des interfaces typées au-delà de "détecter les bugs".
L'autocomplétion IDE est la plus sous-estimée. Quand votre réponse d'API est typée, votre éditeur sait exactement quels champs existent et quelle forme ont les objets imbriqués. Vous arrêtez de deviner si c'est user.profilePicture ou user.profile_picture ou user.avatar.
Le refactoring devient beaucoup plus sûr. Si une équipe backend renomme un champ de userId en user_id, chaque endroit dans votre base de code qui référence l'ancien nom échoue à la compilation — pas silencieusement à l'exécution après qu'un utilisateur rencontre une erreur.
Une documentation qui ne se périme pas. Une interface bien nommée est souvent plus utile qu'une documentation écrite.
Le processus manuel vs. l'utilisation d'un générateur
Voici à quoi ressemble l'écriture de types à la main pour une réponse d'API de complexité moyenne :
{
"user": {
"id": 1042,
"email": "alice@example.com",
"name": "Alice Nguyen",
"roles": ["admin", "editor"],
"profile": {
"bio": "Frontend developer based in Berlin.",
"avatarUrl": "https://cdn.example.com/avatars/1042.png",
"joinedAt": "2023-06-15T08:30:00Z"
},
"settings": {
"theme": "dark",
"notifications": {
"email": true,
"push": false
}
}
},
"meta": {
"requestId": "abc-123",
"timestamp": 1712486400
}
}
Collez le même JSON dans l'outil JSON vers TypeScript et vous obtenez ceci en moins d'une seconde :
export interface Root {
user: User;
meta: Meta;
}
export interface User {
id: number;
email: string;
name: string;
roles: string[];
profile: Profile;
settings: Settings;
}
export interface Profile {
bio: string;
avatarUrl: string;
joinedAt: string;
}
export interface Settings {
theme: string;
notifications: Notifications;
}
export interface Notifications {
email: boolean;
push: boolean;
}
export interface Meta {
requestId: string;
timestamp: number;
}
Tout ce que vous avez à faire c'est renommer Root en quelque chose de significatif comme UserResponse.
Cas d'utilisation réels
Typage des réponses d'API
C'est le cas d'utilisation principal. Vous intégrez une API tierce — processeur de paiement, CRM, service météo — et avez besoin de types pour ce qui revient.
Fichiers de configuration
Si votre application lit un fichier de configuration JSON à l'exécution, vous voulez une interface TypeScript correspondante :
// config.interface.ts (généré à partir de config.json)
export interface AppConfig {
database: Database;
redis: Redis;
featureFlags: FeatureFlags;
}
export interface Database {
host: string;
port: number;
name: string;
ssl: boolean;
}
Données de mock pour les tests
Lors de l'écriture de tests, vous avez souvent besoin d'objets mock typés. Générez l'interface à partir de données réelles, puis créez votre mock typé dessus.
Gestion des cas délicats
Valeurs null
JSON autorise null comme valeur, et les générateurs typisent ces champs comme null. Mais en pratique, un champ null signifie généralement que le champ est optionnel :
interface User {
middleName: string | null;
}
Dates
JSON n'a pas de type Date natif. "2023-06-15T08:30:00Z" est une string du point de vue de JSON. Certaines équipes créent un alias de type :
type ISODateString = string;
Unions discriminées
Parfois une API retourne différentes formes selon un champ type ou status. Vous devrez écrire vous-même ces types :
type ApiResult = SuccessResult | ErrorResult;
interface SuccessResult {
type: "success";
data: OrderData;
}
interface ErrorResult {
type: "error";
code: string;
message: string;
}
Meilleures pratiques pour les types générés
Nommer l'interface racine de façon significative
Le générateur la nommera Root ou quelque chose de générique. Renommez-la toujours avant de commiter. UserProfileResponse, CheckoutSessionPayload, ProductListItem.
Conserver les types dans des fichiers dédiés
src/
types/
api/
user.types.ts
order.types.ts
config.types.ts
Zod pour la validation à l'exécution
Les types TypeScript sont effacés à l'exécution — ils ne vous protègent pas si une API retourne réellement des données inattendues. Si vous avez besoin de garanties à l'exécution, envisagez Zod :
import { z } from "zod";
const UserSchema = z.object({
id: z.number(),
email: z.string().email(),
name: z.string(),
});
type User = z.infer<typeof UserSchema>;
Ce que le générateur ne peut pas faire pour vous
Il ne connaît pas votre logique métier. Un champ typé comme number pourrait n'être en pratique qu'un entier positif.
Il travaille à partir d'un instantané. Les types générés reflètent un échantillon de données. Les APIs réelles évoluent.
Il ne peut pas distinguer requis et optionnel. Chaque champ dans l'exemple est traité comme requis.
Il traite toutes les chaînes comme des chaînes. Un champ status qui ne contient que "pending", "active" ou "cancelled" sera typé comme string plutôt que comme le type union littéral plus précis.
Ce ne sont pas des raisons d'éviter l'outil — ce sont des raisons de traiter la sortie comme un brouillon, pas un produit fini. Le générateur gère 80% du travail fastidieux. Vous gérez les 20% qui nécessitent de comprendre la sémantique réelle des données.
Tout mettre ensemble
Le workflow qui a du sens pour la plupart des projets :
- Obtenir un vrai (ou représentatif) exemple de JSON depuis l'API, la configuration ou une autre source
- Coller dans le générateur JSON vers TypeScript
- Renommer
Rooten quelque chose de significatif - Copier la sortie dans le fichier
*.types.tsapproprié - Passer en revue chaque champ : marquer les champs optionnels avec
?, ajouter| nulloù nécessaire, convertir les chaînes littérales en types union là où c'est approprié - Si votre JSON source est minifié et difficile à lire, utilisez le formateur JSON d'abord, puis générez les types
- Si vous maintenez aussi une configuration YAML, le convertisseur JSON vers YAML peut aider à les synchroniser
L'objectif est d'avoir des types TypeScript précis, nommés et organisés sans passer de temps en transcription purement mécanique. Le générateur fait la partie mécanique. Vous apportez le contexte sur ce que les données signifient réellement.
Les types écrits à la main ne sont pas intrinsèquement meilleurs — ils sont juste plus lents et plus susceptibles d'avoir des fautes de frappe. Utilisez l'outil, passez la sortie en revue, et consacrez votre temps aux parties qui nécessitent vraiment un humain.