Traitement JSON en TypeScript : du parsing de base au typage strict
TypeScript, en tant que surcouche de JavaScript, hérite de toutes les méthodes intégrées pour travailler avec JSON, mais y ajoute un puissant système de types. Cependant, le JSON.parse() standard renvoie le type any, ce qui annule tous les avantages du typage statique. Dans cet article, nous allons voir comment traiter JSON de manière sécurisée et efficace en TypeScript, en utilisant à la fois les capacités natives et des bibliothèques spécialisées.
Le problème du JSON.parse standard
Considérons un scénario typique :
const jsonString = '{"name":"Alice","age":30}';const user: any = JSON.parse(jsonString); // type anyconsole.log(user.name.toUpperCase()); // Erreur à l'exécution si name est absentTypeScript ne vous avertira pas si la structure JSON ne correspond pas à celle attendue. La solution consiste à utiliser une assertion de type (type assertion) ou, mieux encore, une validation à l'exécution.
Assertion de type de base (Type Assertion)
La manière la plus simple est de spécifier explicitement le type du résultat :
interface User { id: number; name: string; email?: string;}
const raw = '{"id":1,"name":"Bob"}';const user = JSON.parse(raw) as User;console.log(user.name); // TypeScript sait que c'est une stringInconvénient : si le JSON arrive avec d'autres champs, l'erreur ne se produira qu'à l'exécution. Ce n'est pas sûr lorsqu'on travaille avec des API externes.
Types récursifs et conditionnels pour JSON
TypeScript permet de décrire n'importe quel JSON via un type récursif :
type JSONValue = | string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue };
function safeParse(json: string): JSONValue | Error { try { return JSON.parse(json) as JSONValue; } catch (e) { return new Error('JSON invalide'); }}Cette approche garantit que le résultat sera toujours une structure JSON valide, mais ne vérifie pas la forme spécifique.
La bibliothèque Zod — validation de schéma à l'exécution
Zod est la bibliothèque la plus populaire pour la validation déclarative de données en TypeScript. Elle permet de décrire un schéma et d'en déduire automatiquement le type.
Installation
npm install zodExemple d'utilisation
import { z } from 'zod';
// Décrire le schémaconst UserSchema = z.object({ id: z.number().positive(), name: z.string().min(1), email: z.string().email().optional(), role: z.enum(['admin', 'user']).default('user'),});
// Déduire le type du schématype User = z.infer<typeof UserSchema>;
// Parsing sécuriséconst result = UserSchema.safeParse(JSON.parse(rawJson));
if (result.success) { // result.data est de type User console.log(result.data.name);} else { console.error(result.error.format());}Zod vérifie automatiquement les types, l'obligation des champs, les valeurs par défaut et renvoie des erreurs détaillées. C'est le choix idéal pour traiter les données d'une API REST ou des fichiers de configuration.
La bibliothèque io-ts — approche fonctionnelle
io-ts est basée sur la programmation fonctionnelle et la bibliothèque fp-ts. Elle utilise la composition de codecs pour décoder/encoder les données.
Exemple
import * as t from 'io-ts';import { isRight } from 'fp-ts/lib/Either';
const UserCodec = t.type({ id: t.number, name: t.string, tags: t.array(t.string),});
const decoded = UserCodec.decode(JSON.parse(raw));
if (isRight(decoded)) { // decoded.right — objet typé console.log(decoded.right.name);} else { // decoded.left — erreurs de validation console.log(decoded.left);}io-ts est plus strict et mathématiquement correct, mais nécessite une compréhension des concepts fonctionnels (Either, pipe).