Skip to content
jsonforge.app
Retour au blog
Guide7 min de lecture

Générer des types TypeScript à partir de JSON : un guide pratique

Écrire à la main une interface TypeScript pour une réponse d'API à 40 champs est fastidieux et propice aux erreurs. Une coquille dans un nom de champ casse la sûreté de type sans la moindre erreur de compilation. Générer l'interface à partir d'un échantillon JSON réel est plus rapide et plus précis, à condition de savoir où l'inférence doit deviner.

Comment fonctionne l'inférence JSON-vers-TypeScript

Un générateur parcourt l'arbre de valeurs JSON et mappe chaque type JavaScript vers son équivalent TypeScript : string reste string, number reste number, boolean reste boolean, null devient null (ou fusionne dans une union), les tableaux deviennent T[] où T est déduit des éléments, et les objets deviennent des interfaces imbriquées.

Un échantillon JSON et son interface déduite.
json
{
  "id": 42,
  "email": "ada@example.com",
  "isActive": true,
  "roles": ["admin", "editor"],
  "profile": {
    "displayName": "Ada",
    "avatarUrl": null
  }
}
Sortie TypeScript déduite.
typescript
interface Root {
  id: number;
  email: string;
  isActive: boolean;
  roles: string[];
  profile: {
    displayName: string;
    avatarUrl: string | null;
  };
}

Là où l'inférence doit deviner

Champs optionnels contre toujours présents : un seul échantillon JSON ne peut pas dire à un générateur si avatarUrl est parfois totalement absent (auquel cas ce devrait être avatarUrl?: string | null) ou toujours présent mais parfois null. Donnez au générateur quelques échantillons représentatifs, ou marquez manuellement un champ optionnel après génération si vous savez que l'API l'omet dans certaines conditions.

Nombres qui portent des identifiants ou des quantités : JSON n'a qu'un type number, et TypeScript ne peut pas distinguer un identifiant entier d'un prix à virgule flottante sans contexte supplémentaire. Certains générateurs proposent des types brandés ou des unions littérales pour cela ; la plupart émettent number et vous laissent la distinction.

Tableaux vides : un tableau sans élément ("tags": []) ne donne au générateur aucun indice sur le type des éléments, il retombe donc sur unknown[] ou any[]. Pointez le générateur vers un échantillon où le tableau est rempli, ou annotez-le à la main ensuite.

Unions de littéraux de chaînes : le générateur déduira en simple string un champ status qui prend l'une des valeurs "pending" | "active" | "closed", sauf s'il voit toutes les valeurs possibles dans l'échantillon. Même alors, la plupart des générateurs choisissent par défaut le type string plus large, sauf si vous activez l'inférence d'unions littérales.

Dates : JSON n'a pas de type date, donc les horodatages arrivent en chaînes (ISO 8601) ou en nombres (epoch Unix). Un générateur type un champ date en string ou number, pas en Date, et vous devez encore l'analyser vous-même après la récupération.

La même idée dans d'autres langages

La logique d'inférence (parcourir l'arbre, mapper les types, nommer les objets imbriqués) reste la même pour tout langage cible ; seule la syntaxe de sortie change. Un struct Go reçoit la même correspondance de champs avec des tags de struct pour les noms de clés JSON ; une dataclass Python reçoit des indications de type et un enveloppement Optional[...] au besoin ; une classe Java ou C# reçoit des champs typés et des accesseurs ou des records. Si votre équipe travaille en plusieurs langages contre la même API, générez les types de chacun depuis le même échantillon JSON pour qu'ils restent cohérents.

La même forme en struct Go.
go
type Root struct {
	ID       int     `json:"id"`
	Email    string  `json:"email"`
	IsActive bool    `json:"isActive"`
	Roles    []string `json:"roles"`
	Profile  struct {
		DisplayName string  `json:"displayName"`
		AvatarURL   *string `json:"avatarUrl"`
	} `json:"profile"`
}

Avant de faire confiance aux types générés

Considérez les types générés comme un point de départ et terminez le contrat vous-même. Avant de les committer, confirmez les champs optionnels avec la documentation de l'API (un échantillon ne suffit pas), remplacez par des unions littérales les champs string trop lâches qui contiennent des enums, vérifiez que les champs à tableau vide ont reçu un vrai type d'élément depuis un meilleur échantillon, et régénérez chaque fois que l'API en amont ajoute ou renomme un champ, plutôt que de retoucher l'interface à la main.

FAQ

Un générateur JSON-vers-TypeScript peut-il gérer un champ tantôt chaîne, tantôt nombre ?
Oui, s'il voit les deux variantes parmi les échantillons que vous lui donnez ; il doit alors déduire string | number. Avec un seul échantillon, il ne peut typer que ce qu'il a vu. D'où l'intérêt de générer à partir de plusieurs charges utiles représentatives plutôt que d'un seul exemple de cas nominal.
Les interfaces générées doivent-elles utiliser `interface` ou `type` ?
Pour ce cas d'usage, les deux se valent presque. `interface` est plus courant pour les formes d'objets et prend en charge la fusion de déclarations ; `type` gère mieux les unions et intersections. Les deux conviennent pour des formes de réponses d'API, alors suivez la convention de votre base de code.
Comment garder les types générés synchronisés quand l'API évolue ?
Traitez la génération comme une étape répétable, pas un copier-coller ponctuel : gardez un échantillon réel de réponse versionné (ou récupéré depuis un point de terminaison de staging) et régénérez l'interface chaque fois que l'échantillon change, plutôt que d'éditer à la main le fichier généré. Certaines équipes l'intègrent dans la CI pour détecter automatiquement la dérive.
Cela fonctionne-t-il pour du JSON profondément imbriqué ou récursif, comme une structure d'arbre ?
L'imbrication profonde génère des interfaces imbriquées sans problème. Les structures récursives (un commentaire qui contient des réponses de même forme) exigent un type auto-référentiel, et la plupart des générateurs ne le déduiront pas d'un seul échantillon. Écrivez l'interface récursive à la main une fois, puis réutilisez-la.

Essayez ces outils

Articles associés