JSON en TypeScript
Interfaces fusionnées sur tous les éléments du tableau, pour que l’optionnel le soit vraiment.
Rien de ce que vous collez ne quitte votre navigateur. La liste d’autorisation connect-src en fait une garantie du navigateur plutôt qu’une promesse. Vérifiez-le vous-même
Transformez un payload JSON en interfaces TypeScript. Les objets imbriqués deviennent des types nommés, les tableaux sont fusionnés sur tous leurs éléments, et les formes structurellement identiques sont émises une fois puis réutilisées.
Inférer à partir d’un échantillon relève de la supposition. L’intérêt de cette page, c’est que les suppositions sont visibles.
Les champs optionnels, là où la plupart des générateurs échouent
Un générateur qui lit l’élément zéro d’un tableau et s’arrête produit une interface où tous les champs sont obligatoires. Le premier enregistrement qui omet un champ optionnel échoue alors au contrôle de types, et vous éditez à la main les types générés, ce qui annule tout l’intérêt.
Celui-ci fusionne tous les éléments. Une clé présente dans tous est obligatoire ; une clé présente dans certains est marquée optionnelle avec un point d’interrogation. C’est une information réelle, extraite de votre échantillon, qu’un générateur mono-élément jette.
Les autres décisions
- null devient une union
- Un champ vu comme chaîne et comme null est string | null, pas any et pas string.
- Un tableau vide est unknown[]
- Pas any[]. Un tableau vide ne porte aucune information de type, et any[] désactiverait discrètement le contrôle pour tout ce qui le touche.
- Les formes répétées sont dédupliquées
- Une liste de cinq cents enregistrements produit une interface, pas cinq cents.
- Les noms d’éléments de tableau sont mis au singulier
- Un tableau categories produit une interface Category.
- Les identifiants invalides sont mis entre guillemets
- Une clé has-dash, 2fast ou class devient un nom de propriété entre guillemets.
- Les entiers non sûrs sont signalés
- Un champ typé number alors que l’échantillon contenait un entier supérieur à 2^53-1 est un mensonge, car JavaScript ne peut pas le représenter. La note le dit ; le bon correctif est en amont, en l’envoyant sous forme de chaîne.
Ce qu’un type généré ne vous donne pas
Une interface TypeScript est effacée à l’exécution. Elle dit au compilateur ce que vous attendez et ne fait strictement rien quand l’API envoie autre chose. Pour cela il vous faut un validateur à l’exécution, et le flux honnête pour une API externe est un schéma qui valide et infère le type depuis la même définition.
Zod, Valibot et ArkType font tous cela. Générez ici une interface pour comprendre un payload, puis écrivez le schéma d’exécution pour la frontière que vous ne contrôlez pas.
How to do this in code
La même idée en code, et quoi utiliser à une frontière en laquelle vous n’avez pas confiance.
sh quicktype
quicktype prend en charge de nombreux langages cibles. Passer plusieurs échantillons est l’option qui compte.
npx quicktype --lang ts --just-types --src-lang json payload.json
# Several samples, which is what makes optionality accurate
npx quicktype --lang ts --just-types samples/*.json ts Zod
C’est le motif à adopter à la frontière d’une API. L’interface parle au compilateur ; le schéma dit la vérité.
import { z } from 'zod';
const User = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
verifiedAt: z.string().nullable(),
roles: z.array(z.string()),
});
// One definition, both a runtime check and a static type
type User = z.infer<typeof User>;
const result = User.safeParse(await res.json());
if (!result.success) console.error(result.error.issues); ts Types seuls, depuis un littéral
// If the data is a constant you control, TypeScript can infer
// the type without a generator at all.
const config = {
retries: 3,
endpoints: ['a', 'b'],
} as const;
type Config = typeof config; Questions fréquentes
- Pourquoi un champ est-il optionnel alors que mon API l’envoie toujours ?
- Parce qu’au moins un enregistrement de l’échantillon ne l’avait pas. C’est soit un vrai champ optionnel, soit un échantillon incomplet. Collez un tableau avec davantage d’enregistrements et la réponse s’améliore.
- Faut-il utiliser interface ou type ?
- Pour des formes d’objets, cela ne change presque rien. Les interfaces autorisent la fusion de déclarations et produisent des messages d’erreur un peu plus agréables ; les alias de type peuvent exprimer des unions et des types mappés. Les deux sont disponibles ci-dessus.
- Ces types valident-ils quoi que ce soit à l’exécution ?
- Non. Les types TypeScript sont effacés à la compilation. Ils décrivent ce que vous croyez ; ils ne le vérifient pas. Pour des données qui traversent une frontière réseau, utilisez un validateur d’exécution.