ProgrammationArchitecte TypeScript

Quelle est la différence entre la typage structurel et nominal en TypeScript ? Peut-on réaliser un typage nominal, et si oui, comment ? Quels problèmes cela pourrait-il résoudre ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

TypeScript utilise le typage structurel (structural typing), ou "typage par le type de canard". Pour la compatibilité des types, la structure (signature) est importante, et non le nom ou l'origine du type.

Exemple :

interface Point2D { x: number; y: number; } interface Coord2D { x: number; y: number; } // Ces types sont interchangeables : Point2D et Coord2D, car la structure est identique. const foo: Point2D = { x: 1, y: 2 }; const bar: Coord2D = foo; // OK !

Le typage nominal (nominal typing) : pour la compatibilité des types, le "nom" ou "fabrication" est important, et la structure n'est pas significative.

Dans TypeScript, le typage nominal n'est pas supporté par défaut, mais on peut l'émuler avec des types marqués (branded types) :

type USD = number & { readonly __brand: unique symbol } type EUR = number & { readonly __brand: unique symbol } let priceUSD: USD; let priceEUR: EUR; // priceUSD = priceEUR; // Erreur ! Marques différentes.

Pourquoi appliquer cela ? Par exemple, pour différencier des types ayant la même structure mais ayant des significations différentes — devises, userID/tokenID, grandeurs physiques, etc.


Question piège

Question : Pourquoi le code suivant compile-t-il sans erreurs, bien que Address et UserId soient logiquement des types différents ?

interface Address { value: string; } interface UserId { value: string; } let id: UserId = { value: "test" }; let addr: Address = id; // OK

Réponse : Parce qu'en TypeScript, la structure est importante, et non le nom du type. Les deux types sont simplement des "objets avec value: string".


Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet


Histoire

Projet : Système financier avec des calculs en USD/EUR. Les montants étaient transmis sous forme de number. Un jour, les devises ont été mélangées lors de l'addition — en raison de la typage structurel, TypeScript ne l'a pas détecté. Plus tard, des types marqués ont été introduits pour éviter de telles erreurs à la compilation.


Histoire

Projet : Dans le développement d'une API REST, des objets étaient utilisés pour les identifiants de différentes entités (userId, groupId), les deux ayant un champ value: string. Par erreur, userId était utilisé à la place de groupId, et seules la logique métier sur le serveur détectait l'erreur.


Histoire

Projet : Dans une bibliothèque de parseurs pour DSL, des structures identiques étaient utilisées (type value = { kind: 'num'|'str', value: number|string }). Des structures identiques se mélangeaient entre différentes parties du code, ce qui entraînait des erreurs logiques. Des champs de marque artificielle ont été ajoutés pour la séparation.