Utility types et type guards
Transformer des types existants avec Partial/Pick/Omit/Record, valider des données inconnues avec les type guards (v is T), et découvrir satisfies.
Quand les besoins grandissent, on ne réécrit pas les variantes à la main : on les dérive des types existants.
🛠️ Utility types
Un utility type est un type utilitaire qui transforme un type existant pour en créer une variante. Partial, Pick, Omit, Record sont des utility types built-in de TypeScript.
Partial<T> rend toutes les propriétés de T optionnelles. Utile pour les mises à jour partielles (PATCH).
Pick<T, K> extrait uniquement les propriétés K de T. Utile pour créer une vue légère d'un objet.
Omit<T, K> retire les propriétés K de T. Utile pour exclure un champ (comme password).
Record<K, V> crée un type objet dont les clés sont de type K et les valeurs de type V. C'est un dictionnaire typé.
| Utilitaire | Effet | Cas d'usage |
|---|---|---|
Partial<T> | toutes les clés optionnelles | mise à jour partielle |
Pick<T, K> | ne garde que les clés K | vue légère |
Omit<T, K> | retire les clés K | exclure un champ |
Record<K, V> | clés K pointant vers V | dictionnaire |
interface User {
id: number
nom: string
email: string
}
type UserPatch = Partial<User>
type UserLight = Pick<User, "id" | "nom">
type UserSansEmail = Omit<User, "email">
type UsersById = Record<number, User>Un type dérivé est un type construit à partir d'un autre type existant. Les utility types produisent des types dérivés. Un changement dans le type source se propage automatiquement aux types dérivés.
✅ satisfies
satisfies est un opérateur qui vérifie qu'une valeur respecte un type sans élargir son type inféré. Contrairement à l'annotation : Type, satisfies conserve le type précis de la valeur.
satisfies vérifie qu'une valeur respecte un type sans élargir le type inféré :
const config = {
production: true,
port: 3000,
} satisfies Record<string, boolean | number>
config.production // typé boolean (pas boolean | number)
config.port // typé number🛡️ Type guards
Un type guard (garde de type) est une fonction qui retourne un prédicat v is T. Elle affirme au compilateur que si elle retourne true, alors v est de type T. Le narrowing s'applique dans la branche if.
Un prédicat de type est la syntaxe v is T dans le type de retour d'une fonction. C'est ce qui distingue une type guard d'un simple retour boolean.
Une fonction qui restreint un type via is :
interface User {
id: number
nom: string
email: string
}
function estUser(v: unknown): v is User {
return (
typeof v === "object" &&
v !== null &&
"id" in v &&
typeof (v as User).id === "number" &&
"nom" in v &&
typeof (v as User).nom === "string" &&
"email" in v &&
typeof (v as User).email === "string"
)
}
declare const data: unknown
if (estUser(data)) {
console.log(data.id) // data est User ici
} else {
console.log("Format invalide")
}Utilisez unknown (sûr) et non any (dangereux) dans les type guards. any désactive toute vérification.
import type importe uniquement la déclaration d'un type, sans code au runtime. Le type est éliminé lors du build (tree-shaking).
🔑 keyof et typeof
keyof est un opérateur qui extrait les clés d'un type sous forme d'un union. keyof User donne "id" | "nom" | "email". Utile pour typer des fonctions qui accèdent à une propriété dynamiquement.
interface User {
id: number
nom: string
email: string
}
type ClesUser = keyof User // "id" | "nom" | "email"
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
const user: User = { id: 1, nom: "Alice", email: "alice@exemple.fr" }
const nom = getProperty(user, "nom") // string
const id = getProperty(user, "id") // numbertypeof (en contexte de type) extrait le type d'une valeur existante. typeof maVariable dans une annotation de type donne le type de maVariable. Utile pour typer à partir d'une constante sans la retaper.
const utilisateur = {
id: 1,
nom: "Alice",
email: "alice@exemple.fr",
}
type User = typeof utilisateur
// { id: number; nom: string; email: string }🗺️ Mapped types
Un mapped type est un type qui transforme les propriétés d'un type existant via une boucle sur ses clés. Syntaxe { [K in keyof T]: ... }. Les utility types (Partial, Pick…) sont des mapped types built-in.
Créer un type où toutes les propriétés deviennent optionnelles (équivalent de Partial) :
type MonPartial<T> = {
[K in keyof T]?: T[K]
}Créer un type où toutes les propriétés deviennent readonly :
type MonReadonly<T> = {
readonly [K in keyof T]: T[K]
}Transformer les types des valeurs :
type Stringify<T> = {
[K in keyof T]: string
}
interface User {
id: number
nom: string
age: number
}
type UserString = Stringify<User>
// { id: string; nom: string; age: string }Combiner avec les template literal types :
type User = {
id: number
nom: string
}
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
type UserGetters = Getters<User>
// { getId: () => number; getNom: () => string }📋 Points clés
- Utility type : transforme un type existant (
Partial,Pick,Omit,Record). - Type dérivé : construit à partir d'un autre type, se propage automatiquement.
- satisfies : valide sans élargir le type inféré.
- Type guard (
v is T) : fonction qui prouve un type via un prédicat. - Prédicat : syntaxe
v is Tqui permet le narrowing. - import type : import de type uniquement, éliminé au build.
- keyof : extrait les clés d'un type sous forme d'union.
- typeof (type) : extrait le type d'une valeur existante.
- Mapped type : transforme les propriétés via
{ [K in keyof T]: ... }.
✅ Vérification des acquis
Quiz du chapitre
TypeScript
Terminez le quiz du chapitre pour le marquer comme complété.