TypeScript
Validação de Dados
Zod
Qualidade de Software
Arquitetura

Validation des données avec Zod : pourquoi les types TypeScript ne suffisent pas

Les types TypeScript sont des garanties de compilation, pas des garanties d'exécution. La validation basée sur le schéma avec Zod crée une source unique de vérité entre le type et les données.

Il existe un malentendu coûteux au sein des équipes qui ont adopté TypeScript avec enthousiasme. La conviction qu'en ayant des types, le système est protégé contre les données mal formées. Ce n'est pas. Et cette confusion entre ce que fait le compilateur et ce qu'il ne fait pas est à l'origine de toute une catégorie de bugs qui n'apparaissent qu'en production.

Je veux dissiper directement ce malentendu, car il façonne les décisions architecturales. Quiconque comprend où finissent les types commence à traiter les limites du système avec le soin dont elles ont besoin.

Ce que TypeScript garantit vraiment

TypeScript est un système de types qui vit entièrement au moment de la compilation. Vous écrivez les annotations, le compilateur les vérifie, puis elles sont supprimées. Le JavaScript exécuté en production ne sait pas que cet objet était un Usuario. Pour le moteur d'exécution, il s'agit de n'importe quel objet.

C’est intentionnel et non un défaut. TypeScript a été conçu pour imposer aucun coût au moment de l'exécution. La conséquence est que chaque type de garantie est une promesse concernant le code que vous avez écrit, et non les données qu'il traitera.

Tant que les données naissent dans votre code, la promesse tient. Une fonction qui prend un nombre et renvoie un nombre est protégée par le compilateur du début à la fin. Le problème commence lorsque les données proviennent de l’extérieur.

La frontière où la garantie s'évapore

Pensez à tout ce qui entre dans votre système sans être créé par celui-ci. La réponse d'une API externe. Le corps d'une requête HTTP. Un formulaire rempli par un humain distrait. Une ligne lue depuis base de données après une migration bâclée. Une variable d'environnement. Un message dans une file d'attente.

À tous ces points, vous faites généralement quelque chose qui revient à déclarer que les données que vous recevez sont d'un certain type. Une affirmation. Et c’est là que réside l’erreur : cette affirmation ne vérifie rien. Cela indique simplement au compilateur de vous faire confiance et de passer à autre chose. Si l'API a modifié un champ de nombre en texte, TypeScript continue de croire qu'il s'agit d'un nombre, car vous lui avez dit de le croire.

Le résultat est un système qui a l’air typé mais qui présente des trous sur les bords, là où entre le monde réel. L’erreur ne se produit pas à la frontière, où elle serait facile à diagnostiquer. Cela se produit à trois niveaux de profondeur, lorsque quelque chose essaie d'utiliser ce champ comme si la promesse était vraie. La piste devient froide, la trace de la pile pointe au mauvais endroit et quelqu'un perd son après-midi.

La validation au moment de l'exécution est ce qui manquait

La solution conceptuelle est simple à énoncer et facile à différer : aux points d'entrée, vous devez réellement vérifier, au moment de l'exécution, que les données ont la forme que vous attendez. Ne faites pas confiance, vérifiez.

La validation d'exécution signifie un code qui examine efficacement les données et répond si elles sont valides ou non. Si l'API a promis un numéro et envoyé un texte, la validation crie là, en bordure, avec un message clair, avant que la valeur ne contamine le reste du flux. Vous échangez un bug silencieux et profond contre un échec bruyant et localisé.

Historiquement, cela était laborieux et facile à oublier. Nous avons écrit l'interface TypeScript d'un côté et, de l'autre, une fonction de validation distincte qui vérifiait manuellement les mêmes champs. Deux descriptions de la même chose, détenues par des personnes différentes, à des époques différentes. Ils différaient. Ils diffèrent toujours. Le type disait une chose, le validateur en vérifiait une autre et les données réelles suivaient une troisième règle.

Zod et l'approche schématique d'abord

C'est là que Zod change notre façon de penser le problème. L'idée centrale est d'inverser l'ordre : au lieu d'écrire le type puis un validateur qui essaie de le suivre, vous écrivez un seul schéma, et le type en est automatiquement dérivé.

Vous décrivez une seule fois la forme des données, avec ses règles, champs obligatoires, formats et limites. De ce schéma, Zod extrait deux choses à la fois. L’un est le validateur qui fonctionne en production et examine réellement les données. L'autre est le type statique TypeScript], généré à partir de la même définition, sans que vous ayez à l'écrire à la main.

C’est le gain qui compte : une source unique de vérité. Le type et la validation ne peuvent plus diverger, car ils proviennent du même endroit. Si vous modifiez le schéma, le type change en même temps et tout code qui dépendait de l'ancien format ne pourra pas être compilé. La divergence n’est plus possible par construction, plutôt que d’être évitée par discipline.

Les données externes entrent, traversent le schéma et ressortent de l'autre côté sous la forme d'une valeur que le compilateur peut désormais traiter avec une confiance légitime, car la confiance a été gagnée au moment de l'exécution et non seulement déclarée. L'assertion aveugle devient un véritable contrôle, et dès lors le reste du système est à nouveau protégé par des types.

Qu'est-ce que cela change dans l'esprit de ceux qui décident

Adopter la validation basée sur le schéma n'est pas un choix d'outil, c'est un choix de l'endroit où placer la limite de confiance. La règle que je propose à l’équipe est simple : rien de ce qui vient de l’extérieur n’entre sans passer par un schéma. API, formulaire, banque, file d'attente, configuration. Tout se valide au bord.

Dans cette limite, vous faites entièrement confiance aux types, car ils sont revenus correspondre à la réalité. En dehors de cela, vous ne faites confiance à rien qui n’a pas été vérifié. Cette séparation nette entre le territoire validé et la nature sauvage est ce qui rend le système prévisible. Quiconque travaille avec TypeScript dans des projets avancés sait que le type est un outil de raisonnement, et il ne raisonne bien qu'à partir de données qui ont réellement la forme promise.

Cela vaut la peine d’enregistrer le coût, qui est honnête et modeste. Il y a un effort initial pour modéliser les schémas et une surcharge d'exécution pour valider les données aux bords. En retour, vous éliminez une classe d’erreurs qui coûtent cher précisément parce qu’elles se manifestent tardivement et loin de la cause. Il s'agit de l'un des meilleurs retours sur investissement que je connaisse en matière de qualité logicielle et s'adresse directement aux pratiques plus larges de sécurité des applications Web, car la validation des entrées constitue également la première ligne de défense.

Si vous dirigez une équipe qui a adopté TypeScript mais qui traite toujours les données externes avec des assertions aveugles, cela vaut la peine de revoir les limites du système cette semaine. Le coût de l'ajout d'une validation là où les données entrent est faible, et le coût de ne pas l'avoir est exactement le genre de bug que personne ne veut déboguer le vendredi.

A lire aussi