3e étageTS TypeScriptPièce 4/7Intermédiaire

Unions, littéraux et narrowing

Combine plusieurs types avec une union, restreins-les à des valeurs précises, puis affine le type avec typeof, in et les unions discriminées.

Ce que tu vas apprendre

  • Écrire une union et des types littéraux.
  • Restreindre un type dans le code : c'est le narrowing.
  • Modéliser des cas distincts avec une union discriminée.

Une valeur, plusieurs types possibles

Une quantité d'ingrédient peut être un nombre (2) ou un texte ("une pincée"). Une union accepte l'un ou l'autre, avec une barre verticale.

let quantite: number | string

quantite = 2
quantite = "une pincée"
quantite = true // Erreur

number | string se lit « un nombre ou un texte ». Toute autre valeur est refusée.

Le problème : l'union limite les actions

Avec une union, TypeScript n'autorise que ce qui marche pour tous les types possibles.

function afficher(quantite: number | string) {
  return quantite.toUpperCase() // Erreur
}

toUpperCase existe pour un texte, pas pour un nombre. TypeScript refuse, car il ne sait pas lequel des deux tu as reçu.

Le narrowing avec typeof

Le narrowing (en français, rétrécissement) consiste à vérifier le type dans le code. TypeScript le comprend et restreint le type dans le bloc concerné.

function afficher(quantite: number | string) {
  if (typeof quantite === "string") {
    return quantite.toUpperCase() // quantite : string
  }
  return quantite.toFixed(1)      // quantite : number
}

Dans le if, quantite est un string. Après lui, il ne reste que number. Rien à annoter : TypeScript suit le chemin du code.

Gérer null et undefined

Une valeur parfois absente s'écrit aussi avec une union.

function nomAuteur(auteur: string | null) {
  if (auteur) {
    return auteur.toUpperCase()
  }
  return "Anonyme"
}

Tester if (auteur) écarte null. Attention : cette vérification écarte aussi le texte vide "", ce qui est parfois voulu et parfois non.

Pour ne retirer que null et undefined, compare avec != null. L'opérateur ! en fin de valeur (auteur!) force TypeScript à te croire, sans vérification : à éviter sauf certitude.

Les types littéraux

Un type littéral n'accepte qu'une valeur exacte. Combiné avec une union, il liste les valeurs permises.

type Difficulte = "facile" | "moyen" | "difficile"

function badge(niveau: Difficulte) {}

badge("facile")
badge("simple") // Erreur

C'est une alternative légère à une liste de constantes. L'éditeur propose même les trois valeurs à la saisie.

Les autres façons de rétrécir

D'autres tests permettent le narrowing.

TestSert à
typeof x === "string"Distinguer les types de base
x instanceof DateReconnaître un objet créé par une classe
"duree" in xSavoir si un objet possède une propriété
x === "facile"Comparer à une valeur précise

L'opérateur in aide à distinguer deux formes d'objets.

type Plat = { cuisson: number }
type Boisson = { glacons: boolean }

function detail(produit: Plat | Boisson) {
  if ("cuisson" in produit) {
    return produit.cuisson
  }
  return produit.glacons
}

Les unions discriminées

Quand plusieurs formes d'objets coexistent, ajoute une propriété commune de type littéral. C'est un discriminant, qui indique de quelle forme il s'agit.

type Etape =
  | { type: "preparation"; minutes: number }
  | { type: "cuisson"; minutes: number; four: number }

function resume(etape: Etape) {
  switch (etape.type) {
    case "preparation": return `${etape.minutes} min`
    case "cuisson": return `${etape.four} degrés`
  }
}

Dans chaque case, TypeScript connaît la forme exacte : seule la cuisson a la propriété four.

Si tu ajoutes un type d'étape et oublies un case, tu peux le faire détecter. Ajoute default: const verif: never = etape : l'erreur te rappelle la forme oubliée.

unknown contre any

Une donnée venue d'Internet est de type incertain. TypeScript propose any et unknown.

let a: any = JSON.parse('{"nom":"Crêpes"}')
a.nimporte.quoi // Accepté, plante peut-être

let u: unknown = JSON.parse('{"nom":"Crêpes"}')
u.nom // Erreur : il faut d'abord vérifier

any coupe la vérification. unknown accepte n'importe quelle valeur mais interdit de s'en servir avant de l'avoir vérifiée avec un narrowing.

if (typeof u === "string") {
  console.log(u.toUpperCase())
}
Les types disparaissent à la compilation. Une réponse d'API annotée d'un type n'est pas vérifiée pour autant. Pour une donnée extérieure, préfère unknown et vérifie-la toi-même.

À retenir

  • Une union A | B accepte l'un ou l'autre des types.
  • Un type littéral comme "facile" n'accepte qu'une valeur précise.
  • Le narrowing (typeof, in, instanceof, ===) précise le type dans un bloc.
  • Une union discriminée utilise une propriété littérale commune pour reconnaître chaque forme.
  • unknown oblige à vérifier la valeur avant usage ; any supprime toute vérification.

Pour aller plus loin

Sources officielles

Ensuite

Projet
Lignes de code
Niveau
3e étage
Appartement
TypeScript
Pièce
4/7
Plan n°
ts/04.unions-et-narrowing
Lignes de code · fiches rédigées à partir des documentations officielles, sources citées en bas de chaque page.