Back

Comment typer les réponses d'API en TypeScript

Comment typer les réponses d'API en TypeScript

Tous les développeurs frontend y ont été confrontés : vous récupérez des données depuis une API, accédez à une propriété, et obtenez undefined à l’exécution — alors même que TypeScript ne s’est jamais plaint. Le problème ne vient pas de votre code. C’est que le système de types de TypeScript ne fonctionne qu’au moment de la compilation. Il n’a aucune idée de ce que votre API retourne réellement.

Cet article couvre les patterns pratiques pour typer les réponses d’API en TypeScript, de la définition d’interfaces basiques à la validation à l’exécution et la génération de types pilotée par contrat.

Points clés à retenir

  • Le système de types de TypeScript fonctionne uniquement au moment de la compilation — il ne peut pas valider la structure réelle des données retournées par une API à l’exécution.
  • Utilisez des interfaces ou des alias de types avec des unions discriminées pour définir des formes de réponse claires et attendues.
  • Un wrapper générique pour fetch maintient les sites d’appel propres et rend les types attendus explicites.
  • Pour les données non fiables ou externes, utilisez des bibliothèques de validation à l’exécution comme Zod, Valibot ou ArkType pour combler l’écart entre les types à la compilation et les réponses réelles.
  • Lorsqu’une spécification OpenAPI existe, générez vos types à partir de celle-ci pour maintenir la synchronisation des contrats entre frontend et backend.

Pourquoi TypeScript ne peut pas vous protéger à l’exécution

Lorsque vous appelez response.json(), TypeScript traite généralement le résultat comme unknown (ou parfois any, selon les typages d’environnement). Cela signifie que vous pouvez le caster en ce que vous voulez — et le compilateur vous fera entièrement confiance.

// ❌ TypeScript fait aveuglément confiance à ce cast
const data = (await response.json()) as User
console.log(data.email) // Pourrait être undefined à l'exécution

C’est la limite fondamentale à comprendre : le typage des réponses d’API en TypeScript vous offre une sécurité à la compilation, mais ne valide pas le JSON réel que votre API retourne. Si la structure change, TypeScript ne le saura pas.

Définir les types pour les formes de réponse attendues

La première étape consiste à donner une structure à ce que vous attendez. Utilisez des interfaces ou des alias de types pour décrire la forme de la réponse :

interface User {
  id: string
  name: string
  email: string
}

Pour des API cohérentes, un wrapper générique gère proprement les états de succès et d’erreur :

type ApiResponse<T> =
  | { status: "ok"; data: T }
  | { status: "error"; message: string }

Ce pattern d’union discriminée est plus transparent qu’un type unique avec de nombreux champs optionnels. Lorsque vous vérifiez status, TypeScript affine automatiquement le type — aucun garde supplémentaire nécessaire.

Typer les réponses fetch avec un wrapper générique

Le typage des réponses fetch en TypeScript est plus propre avec une fonction auxiliaire réutilisable :

async function apiFetch<T>(url: string): Promise<T> {
  const response = await fetch(url)
  if (!response.ok) throw new Error(`Erreur HTTP : ${response.status}`)
  return response.json() as Promise<T>
}

// Utilisation
const user = await apiFetch<User>("/api/users/1")

Cela maintient vos sites d’appel propres tout en rendant la forme attendue explicite. Le même pattern fonctionne avec Axios ou tout autre client HTTP.

Validation à l’exécution : combler l’écart

Les assertions de type comme as User sont une promesse au compilateur, pas une garantie. Pour les données externes non fiables — API tierces, contenu généré par l’utilisateur, ou toute réponse que vous ne contrôlez pas entièrement — vous avez besoin d’une validation à l’exécution.

Zod est l’option la plus largement utilisée. Vous définissez un schéma, parsez la réponse et obtenez un résultat entièrement typé :

import { z } from "zod"

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
})

type User = z.infer<typeof UserSchema> // Dérivé du schéma

const data = await response.json()
const user = UserSchema.parse(data) // Lance une erreur si la structure est incorrecte

L’avantage clé : vos types TypeScript et votre validation à l’exécution restent synchronisés car ils dérivent de la même source de vérité.

Alternatives à connaître :

  • Valibot — bundle plus léger, API similaire à Zod
  • ArkType — très performant, syntaxe native TypeScript

Utilisez .parse() lorsque vous voulez lancer une erreur sur des données invalides, ou .safeParse() lorsque vous voulez gérer les erreurs gracieusement sans exceptions.

Typage piloté par contrat avec OpenAPI

Si votre API possède une spécification OpenAPI, vous pouvez générer automatiquement les types TypeScript en utilisant openapi-typescript :

npx openapi-typescript ./api-spec.yaml -o ./types/api.d.ts

Cette approche maintient vos types synchronisés avec votre documentation d’API. Elle est complémentaire à la validation à l’exécution — les types générés gèrent la couche de compilation, tandis que Zod ou Valibot gèrent la couche d’exécution.

Choisir la bonne approche

ScénarioPattern recommandé
API interne que vous contrôlezInterface + assertion de type
Contrat partagé avec le backendTypes générés depuis OpenAPI
API tierce ou non fiableValidation par schéma Zod/Valibot
Besoin des deux couches de sécuritéTypes générés + schéma d’exécution

Conclusion

TypeScript vous offre structure et autocomplétion, mais ne peut pas valider ce qui arrive via le réseau. Pour les API que vous maîtrisez, une interface bien définie et un wrapper fetch typé suffisent. Pour les données externes ou imprévisibles, associez vos types à un validateur à l’exécution comme Zod. Lorsque vous disposez d’une spécification d’API, générez vos types à partir de celle-ci. Ces approches ne sont pas concurrentes — elles fonctionnent mieux ensemble.

FAQ

Non. Le mot-clé 'as' est une assertion de type, pas une vérification à l'exécution. Il indique au compilateur de traiter une valeur comme un type spécifique, mais ne fait rien pour vérifier les données réelles. Si la structure de la réponse d'API diffère de ce que vous avez asserté, vous obtiendrez des erreurs à l'exécution que TypeScript ne peut pas détecter.

Utilisez Zod ou une bibliothèque de validation à l'exécution similaire chaque fois que vous consommez des données provenant d'une source que vous ne contrôlez pas entièrement, comme des API tierces ou des endpoints publics. Si vous possédez à la fois le frontend et le backend et avez une forte confiance dans la structure de réponse, une simple interface avec une assertion de type est souvent suffisante.

Oui, et cette combinaison est recommandée pour une sécurité maximale. Les types générés par OpenAPI vous offrent l'autocomplétion et la vérification de types à la compilation, tandis que les schémas Zod valident les données de réponse réelles à l'exécution. Ensemble, ils couvrent les deux couches du spectre de la sécurité des types.

La méthode response.json() de l'API Fetch retourne par défaut une Promise de any en TypeScript. Cela signifie que le compilateur n'appliquera aucune vérification de type sur le résultat à moins que vous ne l'assertiez ou ne le validiez explicitement. Certaines équipes remplacent cela par des typages plus stricts qui retournent unknown à la place, forçant une validation explicite.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay