Écrire une fonction qui rend le premier élément d'un tableau prend une ligne. La rendre utile pour des nombres comme pour des factures, sans perdre le type au passage, demande autre chose.
C'est le rôle du générique : laisser le type en paramètre, et le récupérer à l'appel.
Définition
Un générique est un paramètre de type. Il se déclare entre chevrons, à côté du nom de la fonction, et se comporte comme une variable dont la valeur est un type, fixée au moment de l'appel.
function premier<T>(liste: T[]): T | undefined {
return liste[0];
}
const n = premier([1, 2, 3]); // number | undefined
const s = premier(["a", "b"]); // string | undefinedPersonne n'a écrit premier<number> : le compilateur déduit T de l'argument reçu. Sans générique, il aurait fallu annoter le retour en any, et toute la chaîne de vérification serait tombée après cet appel.
Contraindre le paramètre de type
Un T libre n'autorise aucune opération, puisqu'il peut tout valoir. Le mot-clé extends restreint l'ensemble des types acceptés, et débloque au passage ce qui devient sûr.
function parCle<T, K extends keyof T>(objet: T, cle: K): T[K] {
return objet[cle];
}
const facture = { id: 42, montant: 19 };
const m = parCle(facture, "montant"); // number
parCle(facture, "client");
// Argument of type '"client"' is not assignable
// to parameter of type '"id" | "montant"'.Deux paramètres de type suffisent ici à décrire une règle que le langage seul ne sait pas exprimer : la clé demandée existe sur l'objet, et le retour est du type de cette clé précise.
Les génériques déjà présents partout
| Écriture | Ce que le paramètre désigne |
|---|---|
| Array<T> | Le type des éléments d'un Array (tableau) |
| Promise<T> | La valeur que rendra une Promise tenue |
| Map<K, V> | Le type des clés puis celui des valeurs d'une Map |
| Record<K, V> | Un objet dont les clés et les valeurs sont annoncées |
| Partial<T> | Le même type, toutes propriétés devenues facultatives |
Ces cinq écritures se croisent dès la première heure de TypeScript. Comprendre le générique, c'est surtout cesser de les lire comme une syntaxe magique.
Questions fréquentes
Pourquoi ne pas simplement écrire any ?
Parce que any éteint la vérification au lieu de la propager. Une fonction qui reçoit any et rend any laisse passer n'importe quelle faute de frappe en aval. Le générique, lui, conserve le lien entre ce qui entre et ce qui sort, ce qui est précisément l'information utile.
Combien de paramètres de type sont raisonnables ?
Un dans la grande majorité des cas, deux quand une correspondance est en jeu, comme une clé et sa valeur. Au-delà de trois, la signature devient plus difficile à lire que le problème qu'elle résout, et une interface nommée fait presque toujours mieux.
Faut-il préciser le type à chaque appel ?
Rarement. L'inférence couvre la plupart des situations, et un type écrit à la main entre chevrons signale souvent que la signature manque de contrainte. Le cas légitime reste l'appel sans argument porteur d'information, par exemple une requête dont seul le retour est typé.