Unions, littéraux et narrowing
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.
| Test | Sert à |
|---|---|
typeof x === "string" | Distinguer les types de base |
x instanceof Date | Reconnaître un objet créé par une classe |
"duree" in x | Savoir 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.
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())
}
unknown et vérifie-la toi-même.À retenir
- Une union
A | Baccepte 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.
unknownoblige à vérifier la valeur avant usage ;anysupprime toute vérification.
Pour aller plus loin
Sources officielles
Ensuite
Objets, interface et type
Décris la forme d'un objet avec un type objet, une interface ou un alias de type, puis choisis entre les deux.
Génériques et types utilitaires
Écris des fonctions et des types réutilisables avec les génériques, puis dérive des types avec Partial, Pick, Omit et Record.
- Projet
- Lignes de code
- Niveau
- 3e étage
- Appartement
- TypeScript
- Pièce
- 4/7
- Plan n°
- ts/04.unions-et-narrowing