L'opérateur satisfies et les unions discriminées bien typées
Deux outils du typage TypeScript restent sous-utilisés une fois les bases acquises : l'opérateur satisfies (depuis TS 4.9) et la construction rigoureuse des unions discriminées. Les deux résolvent un même problème récurrent : garder un typage précis sans sacrifier l'inférence.
Le problème classique de l'annotation
Annoter une variable avec un type large pour la valider fait perdre l'inférence fine des littéraux :
type Couleur = "rouge" | "vert" | "bleu";
const palette: Record<string, Couleur> = {
fond: "rouge",
texte: "bleu",
};
palette.fond.toUpperCase(); // OK, mais palette.fond est vu comme Couleur, pas "rouge"
Avec Record<string, Couleur>, TypeScript oublie que fond existe précisément et autorise palette.absent sans erreur à la lecture. satisfies vérifie la conformité au type tout en conservant le type inféré le plus précis :
const palette = {
fond: "rouge",
texte: "bleu",
} satisfies Record<string, Couleur>;
palette.fond; // typé "rouge", pas Couleur
Toute clé mal orthographiée ou valeur hors de Couleur déclenche une erreur, sans élargir le type final.
Unions discriminées : éviter le champ optionnel piège
Une erreur fréquente consiste à modéliser un état avec des champs optionnels plutôt qu'avec un vrai discriminant :
// À éviter : les combinaisons impossibles restent typables
type Requete = {
statut: "chargement" | "succes" | "erreur";
donnees?: string;
message?: string;
};
Rien n'empêche ici un objet statut: "erreur" avec des donnees définies et pas de message. La version discriminée élimine ces états invalides :
type Requete =
| { statut: "chargement" }
| { statut: "succes"; donnees: string }
| { statut: "erreur"; message: string };
function afficher(r: Requete) {
switch (r.statut) {
case "succes":
return r.donnees; // narrowing correct
case "erreur":
return r.message;
default:
return null;
}
}
Le compilateur narrow automatiquement chaque branche grâce au champ discriminant commun (statut), sans cast ni vérification manuelle.
À retenir
- satisfies valide un objet contre un type sans en élargir l'inférence, contrairement à une annotation classique (
: Type). - Une union discriminée avec un champ littéral commun bat systématiquement des champs optionnels pour représenter des états mutuellement exclusifs.
- Ces deux techniques se combinent bien avec
as constpour figer des littéraux avant de les faire vérifier parsatisfies.