Encore une chose… à propos des types (et TypeScript)

Pour faire suite à mon dernier article sur l'exhaustivité des types, et quelques messages divers, je vous propose de représenter une couleur.

Pour ça, on va utiliser une représentation hexadécimale, vous savez, comme en CSS avec #7FFF00.

D'ailleurs, en synthèse additive, une couleur, ça se décompose généralement en 3 canaux :

type ColorCanal = typeof Red | typeof Green | typeof Blue;

Chaque canal représente une quantité de couleur :

  • Red : 7F ;
  • Green : FF ;
  • Blue : 00.

La quantité va de 00 à FF, c'est une base 16, il y a donc 16 × 16 possibilités par canal, soit 256 :

type HexaDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F';
type HexaCanalColor = `${HexaDigit}${HexaDigit}`;

On vient d'utiliser un Template Literal pour assembler deux HexaDigit et donc représenter les nombres de 00 à FF.

On pourrait se dire qu'avec ce Template Literal, on peut représenter la couleur sous forme hexadécimale via :

type HexaColor = `#${HexaCanalColor}${HexaCanalColor}${HexaCanalColor}`;

Cependant, nous sommes accueillis avec l'erreur : TS2590: Expression produces a union type that is too complex to represent.. Cette erreur peut arriver lorsqu'il y a plus de 100 000 possibilités pour un Union Type.

Or ici, lorsqu'on déclare ce type, on essaie de représenter l'ensemble des couleurs possibles en RGB soit 256 × 256 × 256 couleurs : 16 777 216.

Je vous propose alors d'utiliser nos canaux et de faire :

type Color = Record<ColorCanal, HexaCanalColor>;

Ici, on définit une structure qui a en clé ColorCanal et, pour chacun des canaux, HexaCanalColor en valeur.

Ce qui permettra de fabriquer à nouveau la couleur avec :

const hexaFrom = (color: Color): string => `#${color[Red]}${color[Green]}${color[Blue]}`;

Ce qui donne, pour notre couleur #7FFF00 :

expect(
  hexaFrom({
    [Red]: '7F',
    [Green]: 'FF',
    [Blue]: '00',
  })
).toBe('#7FFF00');

En résumé, nous venons de voir :

  • Un rappel avec deux utilisations des Union Type sur ColorCanal et HexaDigit ;
  • La notion de Template Literal et de ses limites avec HexaCanalColor et HexaColor ;
  • Une utilisation des Record<Keys, Type> avec de l'exhaustivité sur les Keys et les Values avec Color.

Et peut-être aussi quelques notions sur la représentation des couleurs !