# Émulation de typage nominal en TypeScript

> Le typage structurel est limitatif lorsqu'on veut faire de la programmation défensive. Heureusement, il est possible d'émuler un typage nominal (branding).

- Author: Mathieu Eveillard
- Published: 2023-03-01
- Updated: 2025-12-07
- Tags: Artisanat logiciel
- Canonical: https://www.mathieueveillard.com/fr/blog/typage-nominal/
- Translation (en): https://www.mathieueveillard.com/blog/typage-nominal.md

![Émulation de typage nominal en TypeScript](https://www.mathieueveillard.com/blog/2023-03-01-typage-nominal/typage-nominal.webp)

À la réflexion, ce titre sonne un peu « Les chevaliers de l'an mille au lac de Paladru »… Vous avez la ref' ? Laissez-moi vous guider, en commençant par quelques définitions.

## Typage nominal ou structurel

On parle de **typage nominal** lorsqu'un type est défini par son nom. Java propose un typage nominal. Si 2 types ont des noms différents, par exemple `Numerator` et `Denominator`, le compilateur les considérera comme différents quand bien même ces types seraient identiques dans les faits (même comportement).

Cela s'oppose au **typage structurel**, dans lequel un type est défini par sa structure. TypeScript propose un typage structurel. Par exemple, toujours au sujet des fractions, les types `Numerator` et `Denominator` définis ci-dessous sont entièrement substituables du point de vue du compilateur :

```typescript
type Numerator = {
  value: number;
};

type Denominator = {
  value: number;
};
```

On parle de _duck typing_, parce que si ça vole et que ça fait « coin coin » comme un canard, c'est que c'est un canard 🦆

## Limites du typage structurel

Le choix du typage structurel par les créateurs de TypeScript présente de nombreux avantages, qui sont largement documentés sur le Web. Malheureusement, ce choix s'avère limitant pour qui veut faire de la [programmation défensive](https://en.wikipedia.org/wiki/Defensive_programming), qui vise à « assurer le fonctionnement continu d'un logiciel dans des circonstances imprévues ».

Cette approche suppose un état d'esprit (à juste titre) paranoïaque, qui nous interdit de faire confiance à qui que ce soit, utilisateur ou même développeur : que ce soit intentionnel ou non, des données vous parviendront tôt ou tard, qui mettront le programme en échec. Exemple : la fameuse division par 0.

L'idée est donc d'effectuer les validations nécessaires le plus tôt possible et d'encapsuler la donnée validée dans une abstraction que l'on pourra manipuler en toute sécurité dans la suite du programme. D'où la recommandation d'encapsuler les primitives du langage (`string`, `number` etc.) et de faire émerger les abstractions métier correspondantes (Domain-Driven Design mon Amour 💜).

Ainsi, nous ne saurions nous contenter des types `Numerator` et `Denominator` précédemment définis. Ces types étant identiques du point de vue du compilateur, ils peuvent être intervertis par erreur du développeur ou de l'utilisateur. Tôt ou tard, un dénominateur sera pris pour un numérateur (problématique), ou réciproquement (encore plus problématique car cela pourrait conduire à une division par 0).

Autrement dit, **nous voudrions les garanties du typage nominal, tandis que nous travaillons avec un typage structurel**.

Que faire ?

## Émulation de typage nominal

Eh bien **nous pouvons toujours émuler un typage nominal**. Pour cela, nous allons utiliser :

- Un symbole (`DENOMINATOR_SYMBOL`), que nous aurons soin de _ne pas_ exporter ;

- Un constructeur (`createDenominator`), fonction qui accède au symbole et l'utilise dans l'objet qu'elle crée. Ce constructeur effectue les contrôles sans lesquels il serait abusif de parler de dénominateur.

Ainsi :

- Les types `Numerator` et `Denominator` ne sont plus intervertibles ;

- Créer un objet `Denominator` n'est possible qu'en passant par le constructeur, interdisant de ce fait des dénominateurs nuls.

Elle est pas belle, la vie ? 🏝️🍸

Le code complet est disponible sur GitHub : https://github.com/mathieueveillard/nominal-typing-emulation

## Branding / flavouring

J'ai montré cette approche en prenant l'exemple d'objets (`type Numerator = { value: number; };`), mais dans la « vraie vie », quand il n'y a qu'une seule valeur à encapsuler, ce qui est notamment le cas des identifiants, on voudra probablement utiliser une version plus légère, appelée _branding_ ou _flavouring_.

```typescript
type Branded<T> = string & { __brand: T };
```

Que l'on utilisera comme suit :

```typescript
const CLIENT_SYMBOL = Symbol();

type ClientId = Branded<typeof CLIENT_SYMBOL>;
```

L'idée est la même, sauf que l'on travaille sur une primitive du langage (ici : `string`). C'est plus facile à manipuler qu'un objet, puisque le type ainsi créé est un sous-type de `string`.
