Convertisseur JSON ⇄ TypeScript Interface
Convertissez votre JSON en interfaces TypeScript en un clic. Détection automatique des types, interfaces imbriquées, options readonly/optional/export. Idéal pour typer vos réponses API Angular.
Pourquoi typer ses réponses API en Angular
Lorsque vous consommez une API REST dans Angular, la réponse JSON est dynamique par nature.
Sans typage explicite, TypeScript traite ces données comme any, ce qui annule
tous les bénéfices du typage statique : pas d'autocomplete, pas de vérification à la compilation,
risques d'erreurs runtime.
Définir une interface TypeScript pour chaque réponse API vous apporte :
- ✅ Autocomplétion dans votre IDE (VS Code, WebStorm)
- ✅ Détection d'erreurs à la compilation, pas au runtime
- ✅ Refactoring sûr — renommer un champ propage l'erreur partout
- ✅ Documentation vivante — l'interface décrit le contrat de l'API
- ✅ Tests plus fiables — les mocks respectent le type attendu
// ❌ Sans typage — dangereux : TypeScript ne peut pas valider
this.http.get('/api/users').subscribe(data => {
// data est de type 'any' — aucune vérification possible
console.log(data.name); // Peut étre undefined à runtime
console.log(data.emaill); // Faute de frappe non détectée !
});
// ✅ Avec interface — sûr et autodocumenté
interface User {
id: number;
name: string;
email: string;
isActive: boolean;
}
this.http.get<User[]>('/api/users').subscribe(users => {
users.forEach(u => console.log(u.name)); // Typé et validé par le compilateur
// u.emaill provoque une erreur TS2339 à la compilation
});
Algorithme JSON → TypeScript expliqué
La conversion JSON vers TypeScript repose sur une analyse récursive de chaque valeur. Voici comment l'outil infère les types :
| Valeur JSON | Exemple | Type TypeScript généré |
|---|---|---|
number | 42, 3.14 | number |
string | "Alice" | string |
boolean | true, false | boolean |
null | null | null |
| Tableau homogène | ["a", "b"] | string[] |
| Tableau mixte | [1, "a"] | (number | string)[] |
| Tableau vide | [] | unknown[] |
| Objet imbriqué | {"city": "Paris"} | Interface dédiée |
address → Address).
// Exemple complet de conversion JSON → TypeScript
// Entrée JSON
// {
// "id": 1,
// "name": "Alice",
// "tags": ["admin", "user"],
// "address": { "city": "Paris", "zipCode": "75001" },
// "metadata": null
// }
// Sortie générée par l'outil
// Interface imbriquée générée en premier
export interface Address {
city: string; // inféré depuis "Paris" (string)
zipCode: string; // inféré depuis "75001" (string)
}
// Interface racine avec référence à Address
export interface Root {
id: number; // inféré depuis 1 (number)
name: string; // inféré depuis "Alice" (string)
tags: string[]; // inféré depuis ["admin","user"] (string[])
address: Address; // sous-objet → interface dédiée
metadata: null; // null conservé (option nullable activée)
}
Utilisation avec Angular HttpClient
Une fois votre interface générée, intégrez-la directement dans votre service Angular.
Le générique <T> de HttpClient accepte votre interface
pour typer automatiquement la réponse.
// user.interface.ts — Interface générée par l'outil
export interface Address {
street: string;
city: string;
zipCode: string;
}
export interface User {
id: number;
name: string;
email: string;
isActive: boolean;
tags: string[];
address: Address;
}
// Réponse paginée générique réutilisable
export interface ApiResponse<T> {
status: number;
message: string;
data: T[];
pagination: Pagination;
}
export interface Pagination {
page: number;
perPage: number;
total: number;
}
// user.service.ts — Service Angular avec typage complet
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User, ApiResponse } from './user.interface';
@Injectable({ providedIn: 'root' })
export class UserService {
// URL de base de l'API
private readonly apiUrl = '/api/v1';
constructor(private http: HttpClient) {}
// Récupère la liste paginée des utilisateurs
getUsers(page = 1): Observable<ApiResponse<User>> {
return this.http.get<ApiResponse<User>>(
`${this.apiUrl}/users?page=${page}`
);
}
// Récupère un utilisateur par son identifiant
getUserById(id: number): Observable<User> {
return this.http.get<User>(`${this.apiUrl}/users/${id}`);
}
}
// users.component.ts — Consommation typée dans le composant
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';
import { User } from './user.interface';
@Component({
selector: 'app-users',
template: `
<ul>
<!-- TypeScript garantit que user.name existe et est un string -->
<li *ngFor="let user of users">
{{ user.name }} — {{ user.email }}
</li>
</ul>
`
})
export class UsersComponent implements OnInit {
// Tableau typé : TypeScript connaît la structure exacte
users: User[] = [];
constructor(private userService: UserService) {}
ngOnInit(): void {
this.userService.getUsers().subscribe(response => {
// response.data est de type User[] — autocomplétion complète
this.users = response.data;
});
}
}
*.interface.ts
dédié par entité métier (ex: user.interface.ts, product.interface.ts)
et exportez toutes vos interfaces depuis un fichier index.ts centralisé.
Interfaces imbriquées avancées
Les APIs réelles retournent souvent des structures complexes avec plusieurs niveaux d'imbrication. L'outil gère automatiquement ces cas en créant une interface par niveau d'objet.
// JSON complexe avec 3 niveaux d'imbrication
// {
// "order": {
// "id": "ORD-001",
// "customer": {
// "id": 42,
// "billing": { "country": "FR", "vat": "FR12345" }
// },
// "items": [{ "sku": "ANG-01", "qty": 2 }]
// }
// }
// Interfaces générées (des plus profondes aux plus hautes)
export interface Billing {
country: string; // 3e niveau
vat: string;
}
export interface Customer {
id: number; // 2e niveau
billing: Billing; // référence vers l'interface Billing
}
export interface Items {
sku: string; // interface pour les éléments du tableau
qty: number;
}
export interface Order {
id: string; // 1er niveau
customer: Customer; // référence vers Customer
items: Items[]; // tableau d'interfaces Items
}
export interface Root {
order: Order; // interface racine
}
Record<string, unknown> — utile quand
vous ne voulez pas multiplier les déclarations pour des objets simples.
Zod & class-validator pour la validation runtime
Les interfaces TypeScript ne valident qu'à la compilation. Au runtime, si l'API change son format (déploiement serveur, vérsion API), TypeScript ne peut pas le détecter. Des bibliothèques de validation runtime complètent parfaitement les interfaces.
Zod est la solution la plus populaire en 2024 pour ce cas d'usage.
// Avec Zod : validation runtime + inférence TypeScript automatique
// npm install zod
import { z } from 'zod';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
// Schéma Zod — définit la forme et valide à l'exécution
const UserSchema = z.object({
id: z.number(), // doit être un number
name: z.string(), // doit être une string
email: z.string().email(), // doit être un email valide
isActive: z.boolean(), // doit être un boolean
tags: z.array(z.string()),
metadata: z.nullable(z.string()), // string ou null
});
// TypeScript infère automatiquement le type depuis le schéma
type User = z.infer<typeof UserSchema>;
// Équivalent à :
// interface User { id: number; name: string; email: string; ... }
// Utilisation dans un service Angular
getUser(id: number) {
return this.http.get(`/api/users/${id}`).pipe(
// Valide la réponse API au runtime — lève une erreur si le format change
map(data => UserSchema.parse(data))
);
}
// Avec class-validator (NestJS / Angular côté formulaire)
// npm install class-validator class-transformer
import { IsNumber, IsString, IsEmail, IsBoolean, IsArray } from 'class-validator';
import { plainToInstance, Transform } from 'class-transformer';
// Classe DTO avec décorateurs de validation
export class UserDto {
@IsNumber()
id!: number;
@IsString()
name!: string;
@IsEmail()
email!: string;
@IsBoolean()
isActive!: boolean;
@IsArray()
@IsString({ each: true })
tags!: string[];
}
// Transformer la réponse API brute en instance typée et validée
import { validate } from 'class-validator';
async function parseUser(rawData: unknown): Promise<UserDto> {
const user = plainToInstance(UserDto, rawData);
const errors = await validate(user);
if (errors.length > 0) {
throw new Error('Données API invalides : ' + JSON.stringify(errors));
}
return user;
}
Bonnes pratiques de typage TypeScript
Voici les règles essentielles pour un typage TypeScript robuste dans vos projets Angular :
- Un fichier par entité —
user.interface.ts,product.interface.ts. Ne regroupez pas tout dans un seul fichier. - Préfixez vos interfaces — Certains projets utilisent
IUsermais la convention Angular recommandeUsersans préfixe. - Utilisez
readonlypour les données immuables — Les réponses API ne doivent pas être modifiées directement. - Typage des tableaux — Préférez
User[]àArray<User>(plus lisible). - Évitez
any— Utilisezunknownsi le type est vraiment inconnu, puis affinez avec un type guard. - Documentez avec des commentaires JSDoc — Les commentaires apparaissent dans l'autocomplete VS Code.
// ❌ Anti-pattern — à éviter absolument
interface User {
[key: string]: any; // Annule tout le bénéfice du typage
}
// ✅ Bonne pratique — typage strict avec JSDoc
export interface User {
/** Identifiant unique de l'utilisateur (auto-incrémenté) */
readonly id: number;
/** Nom complet affiché dans l'interface */
name: string;
/** Adresse email — doit être unique en base */
email: string;
/** Rôle de l'utilisateur dans l'application */
role: 'admin' | 'editor' | 'viewer'; // Union type littérale
/** Date de création au format ISO 8601 */
createdAt: string;
/** Adresse postale — optionnelle */
address?: Address;
}
// Type guard pour valider une donnée inconnue
function isUser(data: unknown): data is User {
return (
typeof data === 'object' &&
data !== null &&
typeof (data as User).id === 'number' &&
typeof (data as User).name === 'string'
);
}
// Utilisation du type guard
const rawData: unknown = await fetchUser();
if (isUser(rawData)) {
// TypeScript connaît maintenant le type User avec certitude
console.log(rawData.name); // Autocomplétion disponible
}
users = signal<User[]>([]);. Le typage se propage
automatiquement dans tous les templates qui utilisent le signal.
Conclusion
Le typage des réponses API est l'une des pratiques les plus impactantes que vous puissiez
adopter dans un projet Angular. En quelques secondes, cet outil transforme n'importe quel
JSON en interfaces TypeScript propres, avec support des types imbriqués, des tableaux,
des nullables et des modificateurs readonly / optional.
Complétez vos interfaces avec Zod pour la validation runtime, et structurez vos fichiers par entité métier pour un code maintenable sur le long terme. Un projet bien typé est un projet où les bugs se détectent à la compilation, pas en production.
*.interface.ts, et utilisez-la comme
générique dans vos appels HttpClient.get<VotreInterface>().
Votre IDE fait le reste.