Les utility types sont des types génériques intégrés à TypeScript qui transforment un type en un autre. Au lieu d'écrire un second type User avec tous les champs optionnels, vous écrivez Partial<User> ; au lieu de copier trois champs, Pick<User, "id" | "name">. Ils sont globaux, aucun import n'est donc nécessaire.
Quand User change, les quatre types dérivés suivent. La bibliothèque standard de TypeScript (lib.es5.d.ts) déclare 22 utility types. Les sections ci-dessous les listent tous, regroupés selon le genre de type sur lequel ils agissent, avec un lien vers la page détaillée quand elle existe.
Types objets : Partial, Required, Readonly, Pick, Omit, Record
| Utility type | Ce qu'il fait | Exemple |
|---|---|---|
Partial<T> | rend toutes les propriétés optionnelles | Partial<User> pour les données d'une mise à jour |
Required<T> | rend toutes les propriétés obligatoires (retire ?) | Required<Config> une fois les valeurs par défaut appliquées |
Readonly<T> | rend toutes les propriétés readonly | Readonly<State> |
Pick<T, K> | ne garde que les clés K | Pick<User, "id" | "name"> |
Omit<T, K> | retire les clés K | Omit<User, "password"> |
Record<K, V> | un type objet avec des clés K et des valeurs V | Record<"en" | "de", string> |
Required<T> est l'inverse de Partial<T> ; la page Partial couvre les deux, y compris le undefined explicite qu'un spread comme celui-ci laisse passer.
Types union : Exclude, Extract, NonNullable
| Utility type | Ce qu'il fait | Exemple |
|---|---|---|
Exclude<U, M> | retire les membres de l'union affectables à M | Exclude<"a" | "b" | "c", "a"> vaut "b" | "c" |
Extract<U, M> | garde les membres de l'union affectables à M | Extract<string | number, number> vaut number |
NonNullable<T> | retire null et undefined | NonNullable<string | null> vaut string |
Ces trois types agissent sur des unions, pas sur des objets. C'est la différence essentielle avec Pick et Omit, qui prennent un type objet et une liste de ses clés.
Types de fonctions et de classes : Parameters, ReturnType et autres
| Utility type | Ce qu'il fait | Exemple |
|---|---|---|
ReturnType<F> | le type de retour d'un type fonction | ReturnType<typeof createStore> |
Parameters<F> | les types des paramètres sous forme de tuple | Parameters<typeof fetchPage>[0] |
ConstructorParameters<C> | les paramètres du constructeur d'une classe sous forme de tuple | ConstructorParameters<typeof Point> |
InstanceType<C> | le type d'instance que crée un constructeur | InstanceType<typeof Point> |
ThisParameterType<F> | le type du paramètre this d'une fonction | ThisParameterType<typeof greet> |
OmitThisParameter<F> | le type fonction sans son paramètre this | le type de greet.bind(obj) |
ThisType<T> | fixe le type de this dans les méthodes d'un littéral objet | utilisé avec noImplicitThis dans les API de type builder |
NoInfer<T> | empêche un paramètre de type d'être inféré depuis cette position | fallback: NoInfer<C> |
typeof createOrder est nécessaire parce que ces utilitaires prennent un type, et createOrder est une valeur. Il en va de même pour les classes : typeof Point est le type du constructeur, alors que Point seul, employé comme type, désigne déjà le type d'instance.
NoInfer contrôle d'où un générique tire son type :
Sans NoInfer, TypeScript inférerait C à partir des deux arguments et l'élargirait en "red" | "green" | "blue", si bien que la faute de frappe dans la valeur de repli serait acceptée.
Types de chaîne : Uppercase, Lowercase, Capitalize, Uncapitalize
| Utility type | Ce qu'il fait | Exemple |
|---|---|---|
Uppercase<S> | met un type littéral de chaîne en majuscules | Uppercase<"get"> vaut "GET" |
Lowercase<S> | le met en minuscules | Lowercase<"GET"> vaut "get" |
Capitalize<S> | met le premier caractère en majuscule | Capitalize<"name"> vaut "Name" |
Uncapitalize<S> | met le premier caractère en minuscule | Uncapitalize<"Name"> vaut "name" |
Ces quatre types sont intégrés au compilateur au lieu d'être écrits en TypeScript, et ils sont surtout utiles dans les template literal types, comme `on${Capitalize<E>}` pour des noms de gestionnaires d'événements.
Promesses : Awaited
| Utility type | Ce qu'il fait | Exemple |
|---|---|---|
Awaited<T> | le type obtenu avec await, en déballant les promesses imbriquées | Awaited<Promise<Promise<number>>> vaut number |
Awaited<ReturnType<typeof fn>> est la façon standard de nommer le type du résultat d'une fonction async sans le déclarer à part.
Combiner les utility types
Les utility types s'imbriquent. Quelques combinaisons reviennent assez souvent pour valoir d'être connues par cœur :
Lisez un utility type imbriqué de l'intérieur vers l'extérieur : Readonly<Pick<Post, "id" | "title">> garde d'abord deux propriétés, puis les passe en lecture seule. Les mêmes briques permettent de construire un helper PartialBy qui ne rend optionnelles que certaines clés ; la page Partial l'écrit en entier.
Les utility types ne font rien à l'exécution
Chaque utility type est effacé à la compilation. Une valeur typée Omit<User, "password"> peut encore contenir un mot de passe à l'exécution si l'objet d'origine en avait un :
Le type limite seulement ce que votre code a le droit de lire. Pour retirer un champ des données, déstructurez-le comme dans les dernières lignes, et pour empêcher la mutation à l'exécution utilisez Object.freeze, pas Readonly. Les types intégrés sont des mapped types et des types conditionnels d'une ligne, les mêmes outils vous permettent donc d'écrire les vôtres.
Questions fréquentes
Que sont les utility types en TypeScript ?
Des types génériques fournis avec TypeScript qui transforment d'autres types : Partial<T> rend toutes les propriétés optionnelles, Pick<T, K> en garde certaines, ReturnType<F> récupère le type de retour d'une fonction, etc. Ils sont déclarés dans la bibliothèque standard, on les utilise donc sans rien importer.
Faut-il importer les utility types ?
Non. Partial, Omit, Record, ReturnType et les autres sont des types globaux issus des fichiers de bibliothèque intégrés à TypeScript. Écrivez Partial<User> n'importe où : ni import ni paquet npm n'est nécessaire.
Quels utility types sont intégrés à TypeScript ?
22, tous déclarés dans lib.es5.d.ts : Partial, Required, Readonly, Pick, Omit, Record, Exclude, Extract, NonNullable, Parameters, ConstructorParameters, ReturnType, InstanceType, ThisParameterType, OmitThisParameter, ThisType, NoInfer, Awaited, Uppercase, Lowercase, Capitalize et Uncapitalize.
Les utility types modifient-ils les objets à l'exécution ?
Non. Ils ne décrivent que des types et sont effacés du JavaScript produit. Omit<User, "password"> ne supprime pas la propriété password, et Readonly<T> ne gèle rien. Pour modifier l'objet réel, écrivez le code : une déstructuration avec rest, Object.freeze, etc.
Peut-on écrire ses propres utility types ?
Oui. Ceux qui sont intégrés sont du TypeScript ordinaire : la plupart sont des mapped types ou des types conditionnels d'une ligne dans lib.es5.d.ts. type Nullable<T> = { [K in keyof T]: T[K] | null } est un utility type personnalisé écrit de la même manière.