Retour au cours

frontend / typescript

satisfies et patterns d'inférence

Leçon 191 exercice

Explication

Ce que vous allez apprendre

  • Comprendre pourquoi une annotation classique (: T) élargit les valeurs littérales
  • Comprendre pourquoi as const fige les littéraux mais désactive la vérification de conformité
  • Utiliser satisfies pour obtenir conformité ET inférence précise en même temps
  • Combiner satisfies et as const dans une seule expression
  • Appliquer satisfies à un cas concret : une table de configuration de routes d'API

Dans quel contexte ?

Une équipe backend maintient une table de configuration des endpoints de son API, un objet avec une dizaine d'entrées { url, methode }. Typée avec une simple annotation Record<string, { url: string; methode: Methode }>, cette table perd la méthode HTTP précise de chaque route : routes.creerUtilisateur.methode devient un vague "GET" | "POST" | "PUT" | "DELETE" au lieu de "POST" exactement, ce qui empêche d'exploiter cette précision ailleurs dans le code (par exemple pour générer automatiquement un type de client API). Typée avec as const seul, une faute de frappe comme methode: "POSTE" compile sans la moindre erreur. satisfies est justement l'outil introduit pour résoudre ce dilemme précis.

Un problème que deux outils résolvent à moitié chacun

Cette leçon répond à une frustration précise que vous avez peut-être déjà rencontrée : quand on annote un objet avec un type explicite, TypeScript élargit souvent les valeurs littérales vers leur type général — une chaîne "rouge" devient simplement string ou le type large Couleur. À l'inverse, figer un objet avec as const conserve bien les valeurs précises, mais désactive complètement la vérification de conformité : une faute de frappe dans une valeur ne sera jamais détectée.

ApprocheConformité vérifiée ?Littéraux précis conservés ?
const x: T = {...} (annotation classique)OuiNon — élargi vers le type général
const x = {...} as constNonOui
const x = {...} satisfies TOuiOui

Bonne pratique

Dès que vous définissez une table de configuration, une palette de couleurs ou toute autre structure de constantes, préférez systématiquement satisfies à une simple annotation de type : vous gagnez la détection des fautes de frappe sans rien perdre de la précision d'inférence.

satisfies : le meilleur des deux mondes

L'opérateur satisfies, ajouté plus récemment au langage, résout ce dilemme : il vérifie que l'objet respecte bien un type donné, comme le ferait une annotation classique, tout en conservant l'inférence la plus précise possible pour chaque valeur, comme le ferait as const. Le résultat : les erreurs de conformité sont détectées à la compilation, et le code qui consomme cet objet ensuite bénéficie de types aussi précis que possible.

Un cas d'usage très concret

Un exemple typique est une table de configuration de routes d'API : on veut être certain que chaque entrée respecte bien la forme attendue, avec une url et une méthode (conformité), tout en gardant la méthode HTTP exacte pour chaque route individuellement plutôt que le type large Methode (précision).

Piège fréquent

satisfies ne change rien au type déclaré de la variable une fois l'expression évaluée : const x = {...} satisfies T donne à x le type le plus précis inféré depuis la valeur, pas T. Si vous avez besoin que x soit explicitement typé T pour une raison précise (comme la compatibilité avec une signature de fonction), une annotation classique reste parfois nécessaire en complément.

Une nuance à retenir

satisfies et as const ne sont pas concurrents mais complémentaires : ils peuvent même se combiner dans une seule expression pour obtenir simultanément la vérification de conformité et le gel total des littéraux. C'est l'un des ajouts les plus appréciés des dernières versions de TypeScript, à privilégier dès que vous définissez une configuration ou une table de constantes structurées.

Commandes & code

satisfies et patterns d'inférence

ts
// Problème : une annotation classique élargit trop et perd les littéraux précis ;
// "as const" seul garde les littéraux mais ne vérifie AUCUNE conformité de forme.
type Couleur = "rouge" | "vert" | "bleu";

// Avec une annotation classique : perd les littéraux, devient "string"
const paletteAnnotee: Record<string, Couleur> = {
  fond: "rouge",
  texte: "bleu",
};
paletteAnnotee.fond; // typé "Couleur", pas précisément "rouge"

// Avec "as const" seul : garde les littéraux mais AUCUNE vérification de conformité
const paletteConst = {
  fond: "rouge",
  texte: "orange", // erreur silencieuse : "orange" n'est pas une Couleur valide, mais ça compile
} as const;

// "satisfies" : vérifie la conformité au type ET garde l'inférence littérale la plus précise
const palette = {
  fond: "rouge",
  texte: "bleu",
} satisfies Record<string, Couleur>;
palette.fond; // typé "rouge" (littéral exact), pas juste "Couleur"
// const invalide = { fond: "jaune" } satisfies Record<string, Couleur>;
// Error : "jaune" n'est pas assignable à type "Couleur"

// Cas pratique : configuration d'endpoints, méthode vérifiée mais URL/méthode précises conservées
type Methode = "GET" | "POST" | "PUT" | "DELETE";
const routes = {
  listerUtilisateurs: { url: "/utilisateurs", methode: "GET" },
  creerUtilisateur: { url: "/utilisateurs", methode: "POST" },
} satisfies Record<string, { url: string; methode: Methode }>;
// routes.listerUtilisateurs.methode est typé "GET" exactement

// satisfies + as const combinés : conformité vérifiée ET littéraux totalement figés
const NIVEAUX = ["debug", "info", "warn", "erreur"] as const satisfies readonly string[];
type Niveau = (typeof NIVEAUX)[number];

// Inférence contextuelle : TS déduit le type des paramètres depuis le contexte d'appel
const gestionnaires: Record<string, (evt: MouseEvent) => void> = {
  clic: (evt) => console.log(evt.clientX), // evt inféré en MouseEvent, sans annotation
};

Résumé

  • satisfies vérifie la conformité à un type sans élargir le type inféré, contrairement à : T.
  • as const fige les littéraux mais ne détecte aucune erreur de forme, contrairement à satisfies.
  • Les deux se combinent pour obtenir conformité ET littéraux figés simultanément.

Exercices pratiques

1 disponible
1

Mission : sécuriser la table de routes d'API

Objectif : Remplacer un as const non vérifié par satisfies pour détecter une méthode HTTP invalide à la compilation, sans perdre la précision d'inférence.

Contexte

Une équipe backend maintient une table routes figée avec as const. Une entrée a methode: "POSTE" (faute de frappe) sans que TypeScript ne s'en plaigne, puisque as const ne vérifie aucune conformité de forme. Ta mission : réparer cette table pour détecter ce genre de faute, sans perdre la précision des types littéraux.

Résoudre l’exercice →