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 silencieusement la sûreté de type au lieu de déclencher une erreur de compilation. Générer l'interface directement à partir d'un échantillon JSON réel est plus rapide et plus précis, à condition de comprendre là 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 sont en réalité des identifiants contre des quantités : JSON n'a qu'un type number, mais 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 se contentent d'émettre number et vous laissent la distinction.

Tableaux vides : un tableau sans aucun exemple dedans ("tags": []) ne donne au générateur rien pour déduire le type des éléments — il retombe typiquement sur unknown[] ou any[]. Pointez le générateur vers un échantillon où le tableau est rempli, ou annotez-le manuellement ensuite.

Unions de littéraux de chaînes : un champ status toujours égal à "pending" | "active" | "closed" sera déduit en simple string, sauf si le générateur voit toutes les valeurs possibles dans l'échantillon — et même alors, la plupart des générateurs choisissent par défaut le type plus large string, sauf si vous activez explicitement l'inférence d'unions littérales.

Dates : JSON n'a pas de type date — les horodatages sont toujours des chaînes (ISO 8601) ou des nombres (epoch Unix). Un générateur typera un champ date en string ou number, pas Date ; vous devez toujours 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 — est identique quel que soit le 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 éventuellement un enveloppement Optional[...] ; une classe Java ou C# reçoit des champs typés et des accesseurs ou des records. Si votre équipe travaille sur plusieurs langages contre la même API, générer les types de chacun depuis le même échantillon JSON les garde alignés entre eux.

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

Les types générés sont un bon point de départ, pas un contrat final. Avant de les committer : confirmez les champs optionnels contre la vraie documentation de l'API (pas juste un échantillon), remplacez les champs string trop lâches qui sont en réalité des enums par des unions littérales, vérifiez que les champs à tableau vide ont bien 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 dans les échantillons qu'on lui donne — il doit en déduire string | number. Avec un seul échantillon, il ne peut typer que ce qu'il a vu. C'est la raison principale 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` ?
Quasi identiques fonctionnellement pour ce cas d'usage. `interface` est plus courant pour les formes d'objets et prend en charge la fusion de déclarations ; `type` est plus souple pour les unions et intersections. Les deux conviennent pour des formes de réponses d'API — choisissez celle qui correspond à la convention existante 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éellement récursives (un commentaire pouvant contenir des réponses de même forme) exigent un type auto-référentiel, que la plupart des générateurs ne déduiront pas automatiquement d'un seul échantillon — vous devrez généralement écrire à la main l'interface récursive une fois, puis la réutiliser.

Essayez ces outils

Articles associés