Kodokon kodokon.com

Types littéraux et unions discriminées

Modélisez les états de votre application avec des unions que le compilateur vérifie à votre place.

8 min · 3 questions

Ouvrir cette leçon dans Kodokon

Un type littéral restreint une valeur à un contenu exact : 'info' plutôt que string. C'est la base d'API impossibles à mal utiliser : le compilateur refuse toute valeur hors liste, et votre éditeur propose l'autocomplétion.

TYPESCRIPT
type Level = "info" | "warn" | "error";

function log(level: Level, message: string): void {
  console.log(`[${level}] ${message}`);
}

log("info", "Serveur demarre");
// log("fatal", "Boom"); -> refuse par le compilateur
Une union de littéraux verrouille les valeurs permises

L'étape suivante consiste à combiner des types d'objets qui partagent une propriété littérale commune, appelée discriminant. Chaque état d'un appel réseau, par exemple, transporte uniquement les données qui le concernent : impossible d'accéder à data pendant le chargement.

TYPESCRIPT
type ApiState =
  | { status: "loading" }
  | { status: "success"; data: string[] }
  | { status: "error"; message: string };

function render(state: ApiState): string {
  switch (state.status) {
    case "loading":
      return "Chargement...";
    case "success":
      return state.data.join(", ");
    case "error":
      return state.message;
  }
}
Union discriminée par la propriété status

Dans chaque case, TypeScript rétrécit le type : après case 'error', state.message existe et state.data n'existe pas. Ce mécanisme, le narrowing, remplace des vérifications manuelles fragiles. Ajoutez une garde d'exhaustivité pour verrouiller l'ensemble.

TYPESCRIPT
function assertNever(value: never): never {
  throw new Error("Cas non gere : " + String(value));
}

function label(state: ApiState): string {
  switch (state.status) {
    case "loading": return "En cours";
    case "success": return "Termine";
    case "error": return "Echec";
    default: return assertNever(state);
  }
}
Garde d'exhaustivité avec le type never

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Dans une union discriminée, quel est le rôle de la propriété commune comme status ?
    • Optimiser les performances à l'exécution
    • Permettre au compilateur de rétrécir le type dans chaque branche
    • Rendre la propriété obligatoire dans le JSON reçu
    • Éviter les doublons de propriétés
  2. Avec type Level = 'info' | 'warn', quel appel de log(level, msg) compile ?
    • log('debug', msg)
    • log('info', msg)
    • log(someString, msg) où someString est de type string
  3. À quoi sert une fonction assertNever(value: never) dans le default d'un switch ?
    • À enregistrer les erreurs en production
    • À transformer un cas oublié en erreur de compilation
    • À convertir la valeur en never
    • À interrompre le programme plus rapidement