Eve Media

Type Branding : distinguer les types qui se ressemblent

Le système de types de TypeScript est structurel : deux types avec la même structure sont interchangeables. Mais parfois, un UserId et un ProductId sont tous deux des strings, et les confondre est un bug. Le Type Branding résout ce problème. Chez Eve Media, nous utilisons cette technique pour du code plus sûr.

Le problème

Imaginez deux fonctions : getUser(userId: string) et getProduct(productId: string). Rien n’empêche d’appeler getUser avec un productId. TypeScript ne voit que des strings. Le bug ne sera découvert qu’à l’exécution.

La solution : branding

On crée des types distincts en ajoutant une propriété « fantôme » qui n’existe pas à l’exécution : type UserId = string & { __brand: ‘UserId’ }. Maintenant, TypeScript distingue UserId de ProductId.

Créer des valeurs branded

On ne peut pas juste assigner une string à un UserId. Il faut une fonction de création : function createUserId(id: string): UserId { return id as UserId }. Cette fonction peut aussi valider le format.

Type Flavoring

Une variante plus souple : le flavoring utilise une propriété optionnelle. type UserId = string & { __flavor?: ‘UserId’ }. Les strings simples sont acceptées, mais les types flavored sont distingués entre eux.

Cas d’usage

IDs de différentes entités (UserId, ProductId, OrderId). Unités de mesure (Meters, Kilometers). États validés (ValidatedEmail vs string). Monnaies (EUR, USD). Partout où la confusion serait un bug.

Validation à la frontière

Le branding est particulièrement utile aux frontières du système : API input, lecture base de données. Validez et brandez à l’entrée, utilisez les types branded partout à l’intérieur.

Impact sur le code

Le code devient auto-documenté. sendEmail(email: ValidatedEmail) dit clairement qu’un email validé est requis. Les erreurs sont détectées à la compilation, pas à l’exécution.

Inconvénients

Overhead mental pour l’équipe non familière. Nécessité de créer des fonctions de création/validation. Les casts peuvent contourner la protection si mal utilisés. À utiliser avec discernement.

Helpers et utilities

Créez un type générique pour simplifier : type Brand<T, B> = T & { __brand: B }. type UserId = Brand<string, ‘UserId’>. Des librairies comme ts-brand ou effet proposent des utilities avancés.

Runtime validation

Le branding est une protection compile-time. Pour la validation runtime, combinez avec Zod ou io-ts. Le type branded peut être le output type du schema de validation.

Conclusion

Le Type Branding rend explicites des distinctions implicites. C’est une technique avancée qui, bien utilisée, élimine des catégories entières de bugs. Le typage fort est l’un des plus grands atouts de TypeScript.

Chez Eve Media, nous écrivons du TypeScript robuste. Contactez-nous pour des applications fiables.