Emulation de typage nominal en TypeScript

https://github.com/mathieueveillard/nominal-typing-emulation
https://github.com/mathieueveillard/nominal-typing-emulation

Peut-être ce titre sonne-t-il un peu "Les chevaliers de l'an mille au lac de Paladru" (vous avez la ref ?), alors laissez-moi vous guider.

Un système de types est dit nominal lorsqu'un type est défini par son nom. Deux types peuvent être identiques dans les faits (mêmes données, même comportement), s'ils ont des noms différents, le compilateur les considérera comme différents.

Cela s'oppose au typage structurel, dans lequel un type est défini par sa structure. On parle de duck typing (très sérieusement), parce que si ça vole et que ça fait "coin coin" comme un canard, c'est que c'est un canard 🦆.

Java est un système de types nominal tandis que TypeScript est un système de types structurel.

Le choix à l'origine de TypeScript a de nombreux avantages, qui sont largement documentés sur le Web. Mais quelles en sont les limites ?

Oui, ce choix peut être limitant dans le cas de la programmation défensive, qui consiste notamment à encapsuler les primitives dans des abstractions ad hoc. Cela nous incite à faire émerger et nommer des notions importantes du métier (#ddd), et cela nous incite à effectuer dès la création de ces objets toutes les validations nécessaires.

Par exemple, lorsqu'on parle de fractions, il est intéressant de créer les types suivants :

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

Ah oui, sauf que… si le typage est structurel, ces types sont identiques 😢 Le risque est donc bien réel d'intervertir numérateur et dénominateur au moment de créer une fraction.

Que faire, alors ?

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 comme clef dans l'objet qu'elle crée. C'est bien sûr l'occasion d'effectuer les contrôles qui s'imposent (vérifier que la valeur n'est pas nulle).

Ainsi :

  • Les types Numerator et Denominator ne sont plus intervertibles ;

  • Créer un objet Denominator n'est possible qu'en passant par la fonction createDenominator, 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

Subscribe to Mathieu Eveillard

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe