Generics en JavaScript : pourquoi en avez-vous besoin et comment les utiliser
Le développement moderne en JavaScript et Node.js nécessite non seulement une écriture rapide du code, mais aussi sa fiabilité. L'un des outils clés assurant la flexibilité et la sécurité des types sont les generics (Generics). Dans cet article, nous allons voir ce que sont les generics, pourquoi ils sont nécessaires dans l'écosystème JavaScript et comment les utiliser efficacement en pratique. Ce matériel sera utile aussi bien aux débutants qu'aux développeurs expérimentés souhaitant approfondir leurs connaissances.
Que sont les Generics et pourquoi en avez-vous besoin ?
Les Generics (types génériques) sont un mécanisme permettant de créer des composants (fonctions, classes, interfaces) qui fonctionnent avec différents types de données, tout en conservant un typage strict. Contrairement au JavaScript classique, qui est dynamiquement typé, les generics viennent du monde TypeScript — une surcouche de JS ajoutant le typage statique.
Pourquoi en avez-vous besoin ? Imaginez que vous écrivez une fonction qui retourne le premier élément d'un tableau. Sans generics, vous devriez utiliser le type any, ce qui vous prive des suggestions de l'IDE et augmente le risque d'erreurs. Avec les Generics, vous pouvez écrire un code universel qui fonctionnera en toute sécurité avec des nombres, des chaînes, des objets et tout autre type.
// Sans generics (dangereux)function getFirst(arr: any[]): any { return arr[0];}
// Avec generics (sûr)function getFirst<T>(arr: T[]): T | undefined { return arr[0];}
const num = getFirst([1, 2, 3]); // type numberconst str = getFirst(['a', 'b']); // type stringAinsi, les generics rendent le code plus réutilisable et type-safe, ce qui est particulièrement important dans les grands projets Node.js.
Comment utiliser les Generics en TypeScript (JavaScript avec types)
Bien que le JavaScript pur ne supporte pas les generics, TypeScript s'intègre parfaitement avec l'écosystème Node.js. Examinons les principaux scénarios d'utilisation.
1. Fonctions génériques
La manière la plus simple est de paramétrer une fonction avec un ou plusieurs types. C'est utile pour les utilitaires travaillant avec des collections.
// Fonction pour fusionner deux tableauxfunction mergeArrays<T>(arr1: T[], arr2: T[]): T[] { return [...arr1, ...arr2];}
const numbers = mergeArrays([1, 2], [3, 4]); // number[]const strings = mergeArrays(['hello'], ['world']); // string[]Ici, <T> est un paramètre de type qui est substitué automatiquement lors de l'appel de la fonction. Vous pouvez également spécifier explicitement le type :
const mixed = mergeArrays<number | string>([1], ['a']); // (number | string)[]2. Classes génériques
Les generics sont parfaits pour créer des structures de données, comme des piles, des files d'attente ou des caches.
class Stack<T> { private items: T[] = [];
push(item: T): void { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
peek(): T | undefined { return this.items[this.items.length - 1]; }}
// Utilisationconst numberStack = new Stack<number>();numberStack.push(10);numberStack.push(20);console.log(numberStack.pop()); // 20
const stringStack = new Stack<string>();stringStack.push('TypeScript');console.log(stringStack.peek()); // 'TypeScript'3. Contraintes de types (Constraints)
Parfois, il est nécessaire de limiter le generic à un ensemble spécifique de types. Par exemple, garantir que l'objet a une propriété length.
// Nous limitons T aux types qui ont une propriété lengthfunction logLength<T extends { length: number }>(item: T): void { console.log(`Longueur : ${item.length}`);}
logLength('Bonjour'); // 7logLength([1, 2, 3]); // 3// logLength(123); // Erreur : number n'a pas de lengthLe mot-clé exten