Si vous fouillez dans les entrailles de mon projet perso Marque Idéale les ami(e)s codeurs, vous allez tomber sur un pattern qui revient de manière quasi obsessionnelle dans capsule.types.ts ou category.types.ts. Que ce soit pour les STYLE_LABELS, les BUDGET_DESCRIPTIONS ou les FREQUENCY_LABELS, j'utilise systématiquement le combo as const satisfies.

Certains diront que c’est de la paranoïa de typage. Peut être… Moi, je me dis surtout que c’est le seul moyen de garantir qu’un ajout de feature le lundi matin ne se transforme pas en écran blanc le lundi après-midi.

Voici pourquoi ce pattern est, pour moi, le final boss du typage d'objets de configuration.

Dans tout projet qui commence a prendre de l’ampleur, on finit par séparer la logique pure (nos unions de types) de la présentation (les labels UI). Dans ma codebase, j'ai ça :

export type BudgetLevel = 1 | 2 | 3;

Et je dois mapper ces IDs avec du contenu textuel. C'est là que le combat commence. Le réflexe de survie qui arrive en premier lieu, c'est probablement d'annoter l'objet :

const BUDGET_LABELS: Record<BudgetLevel, string> = {
  1: '€',
  2: '€€',
  3: '€€€',
};

C'est propre, mais c'est nul pour moi. Parce qu'en faisant ça, vous forcez en quelque sorte TypeScript à "oublier" la précision de vos données. Si vous essayez de faire une fonction qui attend spécifiquement le type '€' (littéral), TS vous répondra : "Désolé, tout ce que je sais, c'est que c'est une string". On a perdu ce que les ami(e)s anglophones appelle la narrowness (la précision).

En plus, l'objet est aussi mutable. Ce qui veut dire que n'importe qui peut par exemple modifier BUDGET_LABELS[1] au runtime.

On se dit alors "OK, je vais utiliser as const pour que tout soit readonly et précis".

const BUDGET_LABELS = {
  1: '€',
  2: '€€',
  // J'oublie volontairement le 3
} as const;

Super pour la précision. TS sait que BUDGET_LABELS[1] est strictement '€'. Par contre, si vous oubliez la clé 3 (qui fait partie de BudgetLevel), TypeScript ne dira absolument rien. Donc vous bouclez sur vos niveaux de budget dans votre composant React, et paf, undefined à l'affichage…

Le petit nouveau, satisfies, lui, vérifie que l'objet respecte un type sans l'écraser.

const BUDGET_LABELS = {
  1: '€',
  2: '€€',
  3: '€€€',
} satisfies Record<BudgetLevel, string>;

On a enfin l'erreur si on oublie une clé ! Mais... l'objet n'est pas readonly et les types sont encore "élargis". Si vous ne mettez pas as const, TS considère une nouvelle fois que vos valeurs sont des string génériques.

C'est là que le combo devient magique. Regardez cette ligne tirée de ma logique de calcul de coût par port sur Marque Idéale :

export const FREQUENCY_WEARS_PER_WEEK = {
  weekly: 1,
  'twice-weekly': 2.5,
  daily: 5,
} as const satisfies Record<WearFrequency, number>;

Pourquoi c’est techniquement parfait ?

  1. Exhaustivité Stricte : Grâce au satisfies Record<WearFrequency, ...>, si je rajoute demain une fréquence 'occasionnal' dans mon union de types sans la définir ici, mon projet refuse de compiler. C'est une sécurité qu’on pourrait qualifier d’active.

  2. Narrowing Absolu : Grâce au as const, TypeScript sait que FREQUENCY_WEARS_PER_WEEK.daily vaut exactement 5, et pas juste "un nombre". Si je l'utilise dans un calcul complexe, TS peut même faire du "constant folding" ou vérifier la validité mathématique plus loin.

  3. Immutabilité Totale : Le as const transforme l'objet en Readonly. Impossible de modifier accidentellement une config en plein milieu de l'exécution.

  4. Zéro "Extra Properties" : Si je tente de rajouter une clé monthly qui n'existe pas dans WearFrequency, le satisfies va m'insulter immédiatement !

Imaginez que je veuille renommer mon style 'workwear' en 'professional'. Je change le type CapsuleStyle. Instantanément, tous mes objets STYLE_LABELS, STYLE_DESCRIPTIONS et mes tableaux de config s'allument en rouge car ils ne "satisfont" plus le contrat.

Sans ce pattern, j'aurais dû faire un "Find and Replace" dans tout le projet en croisant les doigts pour ne rien avoir oublié. Avec as const satisfies, c'est le compilateur qui travaille pour moi.

Comme on dit souvent, on n'écrit pas du code pour qu'il marche aujourd'hui. On l'écrit pour qu'il ne pète pas dans six mois quand on aura oublié la moitié de la logique métier.

L'utilisation de as const satisfies dans la codebase de Marque Idéale n'est pas une coquetterie de puriste. C'est une stratégie défensive. Si vous manipulez des objets de correspondance, des énumérations maison ou des dictionnaires de labels, c'est le seul pattern qui vous offre 100% de sécurité sans sacrifier la précision du typage.

Bref, arrêtez de choisir entre la souplesse et la sécurité. Prenez les deux les ami(e)s codeurs.

Keep Reading