Le match #1 : Enum vs Unions Types - Typescript

Posté le : 5 avril 2021

Jour de match !

Plutôt Enums ou Unions Type en Typescript ? Quel est le plus performant ? Quel est le plus pratique ? Des tips d'utilisation ?

Le match s'annonce des plus intenses. On va donc découvrir comment utiliser les Enums et les Unions Type et quels sont leurs défauts et leurs qualités.


Enumerateurs

La documentation officielle de Typescript nous dit que: un Enum permet aux développeurs de définir un ensemble de constantes nommées et de simplifier la documentation de notre projet.

enum FamilyMembersEnum {
   MOTHER, //0
   FATHER, //1
   SISTER, //2
   BROTHER //3
}

Grâce au code ci-dessus, on a défini 4 constantes qui prennent chacune une valeur (de 0 à 4 par défaut) et qui sont regroupées en un groupe de données. Par défaut, les valeurs associées aux mot-clés seront des number en commençant par 0 dans l'ordre croissant. Il est possible de définir ces valeurs avec des string comme ci-dessous :

enum FamilyMembersEnum {
  MOTHER = 'mother',
  FATHER = 'father'
  ...
}

Unions Type

Si on se réfère à la documentation officielle Typescript, les Literals Type sont des types qui font référence à une string. En utilisant les unions, on peut donc faire référence à plusieurs string.

type FamilyMembersType = 'mother' | 'father' | 'sister' | 'brother'

Avec cette ligne de code, on indique à Typescript que le type FamilyMembers peut prendre uniquement les valeurs indiquées soit : 'mother', 'father', 'sister', 'brother'


Accéder aux valeurs / Accessing

C'est ici que le match commence ! Comment accéder aux valeurs de chacune des solutions ?

Enums

Pour les Enums, il faut faire référence au nom de celui-ci suivi du nom de la constante.

let varEnum: FamilyMembersEnum = FamilyMembersEnum.MOTHER

Dans le cas d'une fonction qui prend en paramètre un Enum, on doit importer l'Enum dans le fichier courant (à gauche).

Union Type

Pour les Unions Type, il faut simplement réécrire la valeur.

let varType: FamilyMembersType = 'mother'

Dans le cas d'une fonction utilisant en argument un Union Type, on aura accès à l'auto-complétion sans devoir importer le type.

1️⃣ points pour les Unions Type


Etendre / Extending

Union Type

Lorsque l'on se sert des Unions Type, on peut utiliser toute la puissance des types et créer des unions d'unions (Unionception ;)).

type RedFruits = 'strawberry' | 'cherry'
type YellowFruits = 'banana' | 'lemon'

type Fruits = RedFruits | YellowFruits //'strawberry' | 'cherry' | 'banana' | 'lemon'

Enums

Ceci n'est tout simplement pas réalisable avec des Enumérateurs.

2️⃣ points pour les Unions Type


Itération / Iterating

Il peut être intéressant dans certains cas d'itérer sur les valeurs définies.

Enums

Avec les Enums, rien de plus simple !

for (const member of FamilyMembersEnum) {
}

/!\ Il n'est pas possible d'itérer sur un Enum déclaré en const

Union Type

Pour les Unions Type, ce n'est pas possible. En effet, lorsque le code typescript est transpilé en Javascript, les types ne sont pas conservés et n'existent plus. Javascript est donc incapable d'identifier les valeurs de l'Union Type.

1️⃣ points pour les Enums


Renommage / Renaming

Union Type

Pour changer la valeur d'un Union Type, pas d'autre choix que de modifier tous les fichiers où vous l'utilisez. Pour les plus malins ;), notre ami VSCode est capable de grandes choses.

Enums

Pour les Enums, on change la valeur au niveau de la déclaration de celui-ci et ... c'est tout !

2️⃣ points pour les Enums


Performances

Union Type

Les types ne sont utilisés que pour la phase de développement et sont donc supprimés lors de la transpilation

// Fichier source
type FamilyMembersType = 'mother' | 'father' | 'sister' | 'brother'
// Après transpilation

Notre type FamilyMembersType disparaît lors de la transpilation

Pour transpiler un fichier typescript : tsc nom_du_fichier.ts

Enums

Pour notre Enum FamilyMembresEnum, la transpilation donne quelque chose de plus complexe.

// Fichier source
 enum FamilyMembersEnum {
   MOTHER = 'mother',
   FATHER = 'father',
   SISTER = 'sister',
   BROTHER = 'brother'
 }
// Après transpilation
var FamilyMembersEnum;
(function (FamilyMembersEnum) {
    FamilyMembersEnum[FamilyMembersEnum["MOTHER"] = 0] = "MOTHER";
    FamilyMembersEnum[FamilyMembersEnum["FATHER"] = 1] = "FATHER";
    FamilyMembersEnum[FamilyMembersEnum["SISTER"] = 2] = "SISTER";
    FamilyMembersEnum[FamilyMembersEnum["BROTHER"] = 3] = "BROTHER";
})(FamilyMembersEnum = exports.FamilyMembersEnum || (exports.FamilyMembers

Si on déclare un Enum avec le mot clé const, l'Enum adoptera le même comportement que les types lors de la transpilation et sera donc voué à disparaître. Les références aux Enums sont directement remplacés par la valeur.

// Fichier source
 const enum FamilyMembersEnum {
   MOTHER = 'mother',
   FATHER = 'father',
   SISTER = 'sister',
   BROTHER = 'brother'
 }
 const member = FamilyMembersEnum.SISTER
 //Après transpilation
 var member = "sister" /* SISTER */;

A condition d'utiliser le mot clé const sur les Enums, égalité parfaite !


Pour finir

Enums

Les Numerics Enums ne sont pas protégés

Si on déclare une fonction avec comme paramètre un Numeric Enum

enum FamilyMembersEnum {
    MOTHER, //0
    FATHER, //1
    SISTER, //2
    BROTHER //3
}

function getFamilyMembers(member: FamilyMembersEnum): void;

getFamilyMembers(30); // <--- ne génère pas d'erreur Typescript

Union Type

Les Unions Type ne sont pas lisibles

En choisissant d'utiliser les unions type, on accepte d'ajouter des valeurs dite "brutes" directement dans le code ce qui complexifie la relecture de code et le refactor.

Conclusion

Nous avons pu observer un match très serré entre les deux joueurs. Même si les Unions Type étaient très bien parti avec une entame de match quasiment parfaite, les Enums ont su se démarquer par leurs qualités. C'est finalement un très beau match nul que nous ont fait vivre ces 2 équipes !

Voici le résumé du match

\EnumsUnions Type
Accéder aux valeurs0️⃣1️⃣
Etendre0️⃣2️⃣
Itération1️⃣0️⃣
Renommage2️⃣0️⃣
Performances2️⃣2️⃣

Vous l'aurez compris, chacune des méthodes a des avantages et des inconvénients.

Mon avis

Je n'ai pas choisi de bannir l'un ou l'autre et j'utilise les 2 solutions dans mes projets. J'utilise les Enums (uniquement en const) dans la plupart des situations car je trouve qu'ils apportent une meilleure lecture du code par leur nom qui définit un groupe et une valeur : GROUPE.VALEUR J'utilise les Unions Type dans les cas qui nécessitent de faire des unions d'union.

Et vous, quelles sont vos utilisations des Unions Type et des Enums ?