Comment fonctionne l'inférence JSON vers TypeScript
L'inférence est une passe unique sur l'arbre JSON analysé. L'outil lit chaque valeur, choisit un type TypeScript pour elle, puis écrit une interface par objet trouvé.
- Analyser l'échantillon JSON avec l'analyseur natif du navigateur et rejeter les saisies malformées avec une indication ligne/colonne.
- Détecter un type TypeScript pour chaque valeur —
string,number,boolean,null, tableau ou objet imbriqué. - Donner à chaque objet imbriqué un nom d'interface dérivé de la clé de propriété parente (de sorte que
user.addressdevient une interfaceAddress). - Fusionner les types d'éléments dans chaque tableau de sorte qu'une liste de
{id: 1}et{id: 2, label: "x"}produise un type union avec les bons champs optionnels. - Appliquer vos options (interface vs. type, readonly, optionnel-annulable) et émettre les déclarations dans l'ordre de dépendance pour que le fichier compile sans références en avant.
Pourquoi générer des types TypeScript depuis JSON ?
- La plupart des bugs de forme sont détectables à la compilation si le type de réponse est écrit. Inférer une interface depuis une vraie charge utile en écrit la plus grande partie pour vous, et le mode `strict` détecte le champ que la documentation a oublié de mentionner.
- Associer des interfaces inférées à un validateur à l'exécution comme Zod ou io-ts donne à la même forme deux rôles : autocomplétion dans l'éditeur en développement et une erreur 400 en bordure quand la production envoie quelque chose d'inattendu.
- Le serveur de langage TypeScript ne fait remonter que les champs qu'il connaît. Une fois l'interface inférée importée, l'autocomplétion fonctionne dès que vous tapez le point — plus de cast `as any` sur la réponse et de grep frustrant dans tout le dépôt.
- Si vous êtes sur le point d'écrire une spec OpenAPI, une interface inférée est une première ébauche rapide du schéma de réponse. Vous voudrez encore des exemples et des contraintes écrits à la main, mais les noms de propriétés et les types sont déjà corrects.
Applications courantes
L'inférence aide le plus quand une vraie charge utile existe mais qu'un schéma n'existe pas.
- Typer les charges utiles de webhooks tiers de Stripe, GitHub ou Twilio avant d'écrire un gestionnaire.
- Amorcer les types pour une API REST interne afin que l'équipe front-end puisse commencer à coder dessus le jour même où le back-end arrive.
- Générer un point de départ pour un schéma Zod, io-ts ou Valibot depuis une réponse d'API observée.
À quoi ressemble la sortie ?
Étant donné un document JSON d'exemple et un nom racine, le générateur produit un arbre d'interfaces, une par objet imbriqué. Pour la saisie ci-dessous avec le nom racine User :
Collez {"id":1,"name":"Alice","tags":["a","b"],"address":{"city":"Paris"}} avec le nom racine User et le générateur produit :
export interface User {
id: number;
name: string;
tags: string[];
address: Address;
}
export interface Address {
city: string;
}
Remarquez qu'address a été promu en une interface nommée à part — c'est la sortie dans l'ordre de dépendance. Le même JSON avec le style de déclaration type à la place émettra export type User = {...} ; avec le commutateur readonly activé, chaque propriété obtient le modificateur readonly.
Options du générateur
Style de déclaration
Choisissez interface (l'idiome TypeScript standard pour les formes d'objets) ou type (pratique si vous aurez besoin de types mappés, types conditionnels ou intersections par la suite). Les deux produisent un comportement à l'exécution identique ; le choix est une préférence de style de code.
Champs optionnels annulables
Quand une valeur échantillonnée est null, le type du champ devient T | null. Activer cette option ajoute aussi un modificateur ? afin que le champ soit optionnel côté TypeScript — utile quand l'API omet parfois la clé entièrement plutôt que de retourner null.
Modificateur Readonly
Préfixe readonly à chaque déclaration de propriété de sorte que l'interface émise corresponde à un modèle de données immuable. Pratique pour les slices d'état Redux, les réponses d'API gelées, ou partout où vous voulez que le compilateur signale les mutations accidentelles.
Est-ce que cela supporte les objets imbriqués et les tableaux ?
Oui. Chaque objet imbriqué devient une interface nommée dérivée de la clé de propriété parente, et les tableaux infèrent le type d'élément depuis leur contenu. Les tableaux d'objets obtiennent une interface par forme d'objet, avec des types union où les formes divergent.
Comment les champs optionnels sont-ils inférés ?
Activez le commutateur « Marquer les champs annulables comme optionnels » et tout champ dont la valeur échantillonnée est null obtient un modificateur ? sur la clé plus | null dans le type. Sans le commutateur, le champ reste requis et le type est simplement T | null.
Est-ce que cela supporte les unions discriminées ?
Les types union basiques apparaissent quand un tableau contient des éléments de formes mixtes ou quand un champ porte à la fois une valeur et null. L'inférence complète d'union discriminée (choisir type ou kind comme étiquette et scinder les variantes) nécessite plusieurs échantillons — c'est prévu mais pas dans la version actuelle.
Puis-je inférer des types depuis plusieurs échantillons JSON ?
Pas encore — l'inféreur actuel lit un échantillon à la fois. Si vous avez deux charges utiles qui devraient partager une interface (par exemple, un endpoint de liste et un endpoint d'élément unique), la solution de contournement pratique est de les fusionner en un seul tableau, de générer depuis cela, puis de renommer les types union résultants. L'inférence multi-échantillon est sur la feuille de route car c'est la seule façon de repérer les champs présents dans une réponse et absents dans une autre.
Collez une charge utile, nommez la racine, copiez les interfaces. L'ensemble du pipeline s'exécute dans votre navigateur, de sorte qu'une API non publiée ou un corps de webhook signé reste sur votre machine.