TypeScript : génériques et types utilitaires
Un générique est un type paramétré : on écrit une fois la logique et le type s’adapte à l’usage.
Fonctions génériques
Section intitulée « Fonctions génériques »function premier<T>(liste: T[]): T | undefined { return liste[0];}
const n = premier([1, 2, 3]); // number | undefinedconst s = premier(['a', 'b']); // string | undefinedconst x = premier<boolean>([true]); // type précisé explicitementT est déduit de l’argument : on précise rarement le type à la main. Un paramètre de type se nomme par convention T, U, ou un nom parlant (TCle, TValeur).
function associer<T, U>(a: T, b: U): [T, U] { return [a, b];}const paire = associer('Alice', 30); // [string, number]Contraintes : extends
Section intitulée « Contraintes : extends »Limiter les types acceptés :
function longueur<T extends { length: number }>(x: T): number { return x.length;}longueur('abc'); // acceptélongueur([1, 2]); // acceptélongueur(42); // erreur : number n'a pas de « length »keyof : clés d’un type
Section intitulée « keyof : clés d’un type »function lire<T, K extends keyof T>(objet: T, cle: K): T[K] { return objet[cle];}
const client = { id: 1, nom: 'Alice' };const nom = lire(client, 'nom'); // stringlire(client, 'age'); // erreur : « age » n'est pas une cléT[K] est le type de la propriété K de T : le type de retour suit la clé choisie.
Interfaces et alias génériques
Section intitulée « Interfaces et alias génériques »interface Resultat<T> { donnees: T; erreur?: string;}
type Reponse<T> = { statut: number; corps: T };
const liste: Resultat<string[]> = { donnees: ['a'] };
interface Depot<T, TId = number> { trouver(id: TId): Promise<T | undefined>; enregistrer(entite: T): Promise<void>;}TId = number donne une valeur par défaut au paramètre de type.
Classes génériques
Section intitulée « Classes génériques »class Pile<T> { private elements: T[] = [];
empiler(element: T): void { this.elements.push(element); }
depiler(): T | undefined { return this.elements.pop(); }
get taille(): number { return this.elements.length; }}
const pile = new Pile<number>();pile.empiler(1);pile.empiler('a'); // erreurVoir POO : polymorphisme et interfaces pour des dépôts génériques.
Types utilitaires
Section intitulée « Types utilitaires »TypeScript fournit des types qui transforment d’autres types :
| Type | Effet | Exemple |
|---|---|---|
Partial<T> |
Toutes les propriétés deviennent facultatives | Mise à jour partielle |
Required<T> |
Toutes obligatoires | |
Readonly<T> |
Toutes en lecture seule | |
Pick<T, K> |
Garde certaines propriétés | Pick<Client, 'id' | 'nom'> |
Omit<T, K> |
Retire certaines propriétés | Omit<Client, 'motDePasse'> |
Record<K, V> |
Objet de clés K et valeurs V |
Record<string, number> |
Exclude<U, V> |
Retire des membres d’une union | |
Extract<U, V> |
Garde des membres d’une union | |
NonNullable<T> |
Retire null et undefined |
|
ReturnType<F> |
Type de retour d’une fonction | |
Parameters<F> |
Types des paramètres (tuple) | |
Awaited<T> |
Type résolu d’une promesse |
interface Client { id: number; nom: string; email: string; motDePasse: string;}
type ClientPublic = Omit<Client, 'motDePasse'>;type MiseAJour = Partial<Omit<Client, 'id'>>;type Resume = Pick<Client, 'id' | 'nom'>;
function modifier(client: Client, changements: MiseAJour): Client { return { ...client, ...changements };}
type Etat = 'ouvert' | 'ferme' | 'archive';type Actifs = Exclude<Etat, 'archive'>; // 'ouvert' | 'ferme'
async function charger() { return { id: 1, nom: 'Alice' };}type Chargement = Awaited<ReturnType<typeof charger>>; // { id: number; nom: string }Dériver les types plutôt que les dupliquer évite qu’ils divergent : si Client change, ClientPublic suit.
Types mappés
Section intitulée « Types mappés »type Drapeaux<T> = { [K in keyof T]: boolean };type Optionnel<T> = { [K in keyof T]?: T[K] };type SansLectureSeule<T> = { -readonly [K in keyof T]: T[K] };
interface Options { couleur: string; taille: number;}type ActivationOptions = Drapeaux<Options>; // { couleur: boolean; taille: boolean }[K in keyof T] parcourt les clés de T. Les modificateurs ?, readonly, -?, -readonly ajoutent ou retirent des propriétés.
Types conditionnels
Section intitulée « Types conditionnels »type EstTableau<T> = T extends unknown[] ? true : false;type A = EstTableau<string[]>; // truetype B = EstTableau<number>; // false
type ElementDe<T> = T extends (infer U)[] ? U : never;type E = ElementDe<number[]>; // number
type SansPromesse<T> = T extends Promise<infer U> ? U : T;infer capture un type dans une condition. Les types conditionnels sont puissants mais difficiles à lire : s’en servir dans des bibliothèques utilitaires, pas dans chaque module.
Types littéraux de gabarit
Section intitulée « Types littéraux de gabarit »type Methode = 'GET' | 'POST';type Route = `/api/${string}`;type Evenement = `${'clic' | 'survol'}:${'bouton' | 'lien'}`; // 'clic:bouton' | 'clic:lien' | 'survol:bouton' | 'survol:lien'
const route: Route = '/api/clients';const mauvaise: Route = '/clients'; // erreurGénériques dans les API courantes
Section intitulée « Génériques dans les API courantes »const liste = new Map<string, number>();const unique = new Set<string>();const promesse: Promise<string[]> = Promise.resolve(['a']);const tableau: Array<{ id: number }> = [];
async function recuperer<T>(url: string): Promise<T> { const reponse = await fetch(url); return (await reponse.json()) as T; // T est affirmé, pas vérifié}const clients = await recuperer<Client[]>('/api/clients');Bonnes pratiques
Section intitulée « Bonnes pratiques »- Un générique doit servir : s’il n’apparaît qu’une fois dans la signature, un type simple suffit.
- Nommer les paramètres de type quand il y en a plusieurs (
TEntite,TId). - Préférer les types utilitaires prédéfinis aux types mappés écrits à la main.
- Éviter les types conditionnels profonds : ils ralentissent le compilateur et dégradent les messages d’erreur.
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
Type 'X' does not satisfy the constraint 'Y' |
Argument de type incompatible avec extends |
Type 'T' is not assignable to type 'U' |
Le compilateur ne peut pas prouver la conversion : contrainte, affinage ou surcharge |
Property 'x' does not exist on type 'T' |
Ajouter une contrainte (T extends { x: … }) |
Generic type 'X' requires N type argument(s) |
Argument de type manquant |
Type instantiation is excessively deep and possibly infinite |
Type récursif ou conditionnel trop profond : simplifier |
| Message d’erreur illisible | Décomposer les types en alias nommés |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Aucune.