Generics en JavaScript : pourquoi et comment les utiliser
Dans le monde du développement moderne avec JavaScript et Node.js, le concept de Generics (types génériques) est de plus en plus courant. Bien que JavaScript natif ne supporte pas le typage strict, des outils comme TypeScript et JSDoc permettent d'intégrer de puissants mécanismes de programmation générique. Dans cet article, nous allons voir à quoi servent les Generics, comment ils fonctionnent dans le contexte de Node.js et comment les utiliser correctement.
Que sont les Generics et pourquoi en a-t-on besoin ?
Les Generics sont un mécanisme qui permet de créer des composants (fonctions, classes, interfaces) qui fonctionnent avec différents types de données tout en conservant un typage strict. Au lieu d'écrire la même fonction pour les nombres, les chaînes et les tableaux, vous écrivez un seul code générique.
Principaux avantages des Generics :
- Sécurité des types — les erreurs de type sont détectées à l'étape de compilation/vérification, et non à l'exécution.
- Réutilisabilité du code — une seule fonction fonctionne avec n'importe quel type de données.
- Lisibilité — le code devient auto-documenté.
- Intégration avec l'IDE — l'autocomplétion et les suggestions de types fonctionnent correctement.
Dans Node.js, les Generics sont particulièrement utiles pour travailler avec des bases de données, des requêtes API, des collections de données et des utilitaires.
Generics en TypeScript (méthode principale)
TypeScript est une surcouche de JavaScript qui ajoute le typage statique. C'est dans TypeScript que les Generics sont implémentés le plus complètement. Examinons la syntaxe de base.
Fonction générique simple
// Sans Generics — on doit utiliser anyfunction identity(arg: any): any { return arg;}
// Avec Generics — le type est conservéfunction identity<T>(arg: T): T { return arg;}
// Utilisationconst num = identity<number>(42); // type numberconst str = identity<string>("hello"); // type stringLes chevrons <T> sont la déclaration du paramètre de type. T est un nom conventionnel, il peut être quelconque (on utilise souvent T, U, V).
Generics avec les tableaux
L'un des cas les plus fréquents est le travail avec les tableaux :
// Fonction qui retourne le premier élément d'un tableaufunction getFirst<T>(arr: T[]): T | undefined { return arr[0];}
const firstNumber = getFirst([1, 2, 3]); // numberconst firstString = getFirst(["a", "b", "c"]); // stringTypes multiples
On peut utiliser plusieurs paramètres de type :
function pair<T, U>(first: T, second: U): [T, U] { return [first, second];}
const result = pair(42, "réponse"); // type [number, string]Contraintes (Constraints) dans les Generics
Parfois, il est nécessaire de limiter les types pouvant être utilisés. Pour cela, on utilise des contraintes avec le mot-clé extends.
// Fonction qui fonctionne uniquement avec les objets ayant une propriété lengthfunction getLength<T extends { length: number }>(arg: T): number { return arg.length;}
getLength("hello"); // 5 — la chaîne a lengthgetLength([1, 2, 3]); // 3 — le tableau a length// getLength(42); // Erreur ! number n'a pas lengthCela est particulièrement utile dans Node.js pour travailler avec les buffers, les chaînes et les tableaux — tout ce qui possède une propriété length.
Generics dans les classes et les interfaces
Les Generics fonctionnent parfaitement non seulement avec les fonctions, mais aussi avec les classes et les interfaces.
Classe générique
Exemple d'une pile simple en Node.js :
class Stack<T> { private items: T[] = [];
push(item: T): void { this.items.push(item); }
pop(): T | undefined { return this.items.pop