Utilitaires de type avancés : Record, Exclude, Extract, NonNullable
Après les bases de TypeScript, plongeons dans les types utilitaires qui vous feront gagner un temps précieux et rendront votre code incroyablement expressif.
Publié le 21 février 2026 • Temps de lecture : 8 min
Introduction : Pourquoi des types utilitaires ?
Dans notre précédent article sur les bases de TypeScript, nous avons vu comment définir des interfaces, des types unions, et même des génériques simples. Mais TypeScript offre une boîte à outils bien plus puissante : les types utilitaires (utility types).
Ces types sont des "fonctions" qui opèrent sur d'autres types pour en créer de nouveaux. Ils permettent d'éviter la répétition, de rendre vos types plus dynamiques et de coller précisément à la logique de votre code.
Prérequis
Cet article est la suite directe de notre Cours TypeScript Débutant. Si vous n'êtes pas familier avec les interfaces, les unions (|) et les génériques, je vous recommande de commencer par là-bas.
1. Record<Keys, Type> : Le dictionnaire typé
Record est un type utilitaire qui construit un type objet dont les clés sont d'un certain type (souvent une union de chaînes) et les valeurs d'un autre type.
Syntaxe
Exemple simple
// On veut un objet qui associe à chaque jour un nombre d'heures de travail
type HorairesTravail = Record<JourSemaine, number>;
const horaires: HorairesTravail = {
lundi: 8,
mardi: 8,
mercredi: 6,
jeudi: 8,
vendredi: 4
}; // ✅ Valide
// const mauvaisHoraires: HorairesTravail = { lundi: 8 }; // ❌ Erreur : il manque mardi, mercredi...
Cas d'usage concret : Configuration ou mapping
type MessageErreur = Record<CodeErreur, string>;
const erreurs: MessageErreur = {
404: "Page non trouvée",
500: "Erreur serveur interne",
403: "Accès interdit"
};
function getMessageErreur(code: CodeErreur): string {
return erreurs[code];
}
🎯 À retenir sur Record
Record est parfait pour créer des objets qui servent de dictionnaires, de mappings ou de configurations, lorsque vous connaissez l'ensemble des clés possibles à l'avance.
2. Exclude<T, U> : Retirer des membres d'une union
Exclude construit un type en prenant tous les membres de l'union T qui ne sont pas assignables à U. En gros, ça exclut certains cas.
Syntaxe
Exemples concrets
// On veut un type pour les statuts "visibles" dans l'UI (sans "supprimé")
type StatutVisible = Exclude<Statut, "supprimé">; // "actif" | "inactif" | "en_attente"
type TypesMélangés = string | number | boolean | null;
type SansNullNiBoolean = Exclude<TypesMélangés, boolean | null>; // string | number
Utile pour restreindre des paramètres
function écouterEvenementsSimples(event: Exclude<Evenement, "scroll" | "resize">) {
// Ne gère que les événements "click" et "hover"
console.log(`Écoute de l'événement: ${event}`);
}
écouterEvenementsSimples("click"); // ✅
// écouterEvenementsSimples("scroll"); // ❌ Erreur: Argument of type '"scroll"' is not assignable...
💡 Exclude fonctionne avec des unions, pas avec des objets. Si vous voulez exclure une propriété d'une interface, regardez du côté de Omit<T, K> (que nous verrons dans un prochain article).
3. Extract<T, U> : L'inverse d'Exclude
Extract fait l'exact opposé d'Exclude : il construit un type en prenant uniquement les membres de l'union T qui sont assignables à U.
Syntaxe
Exemples
// Extraire seulement les types d'erreur
type ErrorResponses = Extract<ResponsesAPI, `error_${number}`>; // "error_400" | "error_404"
// Avec des types plus simples
type T1 = Extract<string | number | boolean, string>; // string
type T2 = Extract<string | number | Function, Function>; // Function (si une fonction est présente)
Cas pratique : Filtrer des types d'événements
| { type: "USER_LOGIN"; userId: string }
| { type: "USER_LOGOUT"; userId: string }
| { type: "PAGE_VIEW"; page: string }
| { type: "ERROR"; message: string };
// Extraire uniquement les événements liés à l'utilisateur
type UserEvents = Extract<EventType, { type: "USER_LOGIN" | "USER_LOGOUT" }>;
// Résultat : { type: "USER_LOGIN"; userId: string } | { type: "USER_LOGOUT"; userId: string }
4. NonNullable<T> : La chasse au null et undefined
NonNullable est un utilitaire simple mais extrêmement utile : il prend un type T et en retire null et undefined.
Syntaxe
Exemple fondamental
type ValeurSûre = NonNullable<ValeurPotentiellementNulle>; // string
Cas d'usage : Après une vérification
C'est très pratique quand vous avez une fonction qui peut retourner null ou une valeur, et que vous voulez créer un tableau ou une variable qui contient seulement les valeurs non-nulles.
// Filtrer les null/undefined
const valeursPropres = valeurs.filter((v): v is NonNullable<typeof valeurs[number]> => v != null);
// Le type de valeursPropres est maintenant string[] (plus de null/undefined)
type RetourRecherche = string | null;
function trouverUtilisateur(id: number): RetourRecherche { /* ... */ }
// Si on est sûr que l'utilisateur existe après une vérification
function traiterUtilisateurExistant(id: number) {
const utilisateur = trouverUtilisateur(id);
if (utilisateur === null) return;
// Ici, TypeScript sait que utilisateur est de type string
const utilisateurSûr: NonNullable<typeof utilisateur> = utilisateur; // string
console.log(utilisateurSûr.toUpperCase());
}
Cas pratiques : Combiner les utilitaires
La vraie puissance arrive quand on commence à combiner ces utilitaires.
Exemple 1 : Gestion d'état d'interface utilisateur
// Types pour les messages associés à chaque statut (sauf "loading" qui n'a pas de message)
type StatutsAvecMessage = Exclude<StatutChargement, "loading">;
type MessagesUI = Record<StatutsAvecMessage, string>;
const messages: MessagesUI = {
idle: "Prêt",
success: "Opération réussie !",
error: "Une erreur est survenue."
}; // ✅ "loading" est bien exclu, pas besoin de message.
Exemple 2 : Configuration de thème avec valeurs par défaut
type Thème = Record<ClésThème, string>;
const thèmeDéfaut: Thème = {
primary: "#007bff",
secondary: "#6c757d",
background: "#ffffff",
text: "#212529"
};
// Fonction qui permet de surcharger le thème, mais avec des valeurs sûres (non null)
function créerThème(surcharge: Partial<Thème>): Thème {
return { ...thèmeDéfaut, ...surcharge };
}
type CouleurSûre = NonNullable<string | null>; // string
🎯 Point clé
Ces utilitaires sont définis dans la bibliothèque standard de TypeScript. Vous n'avez rien à importer. Ils fonctionnent au niveau du système de types et disparaissent à la compilation. Ils sont vos alliés pour une meilleure expressivité sans impact sur le code final.
Conclusion : Vers des types encore plus avancés
Nous avons exploré quatre utilitaires fondamentaux :
- Record : pour créer des dictionnaires et mappings.
- Exclude : pour retirer des membres d'une union.
- Extract : pour ne garder que certains membres d'une union.
- NonNullable : pour éliminer
nulletundefined.
Maîtriser ces outils vous permettra d'écrire des types plus précis et plus proches de la réalité métier de votre application.
🚀 Prochaines étapes
TypeScript ne s'arrête pas là. Voici ce que nous pourrions explorer ensemble dans de futurs articles :
- Pick et Omit : Manipuler les interfaces en choisissant ou excluant des propriétés.
- Partial, Required, Readonly : Rendre les propriétés optionnelles, obligatoires ou en lecture seule.
- Parameters, ReturnType : Extraire les types des paramètres et du retour d'une fonction.
- Awaited : Déballer le type d'une Promise.
- Les template literal types : Créer des types dynamiquement à partir de chaînes.
💡 Le mot de la fin
Ne cherchez pas à tout prix à utiliser ces utilitaires partout. Le but est de rendre votre code plus sûr et plus lisible. Parfois, un type explicite simple est préférable à une gymnastique de types trop complexe. L'équilibre est la clé !
Questions Fréquentes
Où trouver la liste complète des utilitaires TypeScript ?
La documentation officielle de TypeScript est excellente. Consultez la section Utility Types pour une liste complète et à jour.
Puis-je créer mes propres utilitaires de type ?
Absolument ! Avec les génériques et les types conditionnels (T extends U ? X : Y), vous pouvez créer vos propres types utilitaires personnalisés. C'est un sujet avancé, mais extrêmement puissant.
Existe-t-il un utilitaire pour rendre toutes les propriétés d'une interface optionnelles ?
Oui, c'est Partial<T>. Nous le verrons dans le prochain article de cette série.
Ces utilitaires fonctionnent-ils avec les classes ?
Oui, ils fonctionnent avec tous les types, y compris les types de classes.