Que sont les Generics en JavaScript et pourquoi en avez-vous besoin ?
\n\nImaginez que vous écrivez une fonction qui doit fonctionner avec différents types de données — nombres, chaînes de caractères, objets. Sans Generics (génériques), vous devriez soit dupliquer le code, soit utiliser any, perdant ainsi le contrôle des types. Les Generics sont un mécanisme qui permet de créer des composants (fonctions, classes, interfaces) fonctionnant avec n'importe quel type de données, tout en conservant l'information sur le type.
Bien que JavaScript soit un langage à typage dynamique, l'écosystème Node.js utilise activement TypeScript, qui ajoute un typage statique. C'est dans TypeScript que les génériques déploient toute leur puissance. Cependant, comprendre leurs principes est également utile pour le JavaScript pur — surtout si vous écrivez des bibliothèques ou travaillez avec des frameworks modernes.
\n\nDans cet article, nous aborderons :
\n- \n
- Pourquoi les Generics sont nécessaires \n
- Comment déclarer et utiliser les génériques en TypeScript \n
- Des exemples pour Node.js (API, traitement de données) \n
- Les limitations et les meilleures pratiques \n
Les bases des Generics : syntaxe et exemples fondamentaux
\n\nPremier générique : la fonction identity
\n\nL'exemple classique — une fonction qui retourne ce qu'elle reçoit en entrée :
\n\n// Sans Generics\nfunction identity(value: any): any {\n return value;\n}\n\n// Avec Generics\nfunction identity<T>(value: T): T {\n return value;\n}\n\n// Utilisation\nconst num = identity<number>(42); // num — number\nconst str = identity<string>('hello'); // str — string\n\n\nLe symbole <T> est un paramètre de type. Vous pouvez le nommer comme vous voulez (T, U, Item), mais la tradition veut qu'on utilise T (pour Type).
Generics avec plusieurs paramètres
\n\nParfois, il est nécessaire de travailler avec plusieurs types simultanément :
\n\nfunction pair<T, U>(first: T, second: U): [T, U] {\n return [first, second];\n}\n\nconst result = pair<number, string>(1, 'un');\n// result: [number, string]\n\n\nGenerics dans les fonctions fléchées (Node.js)
\n\nDans React ou les utilitaires, on utilise souvent des fonctions fléchées avec des génériques :
\n\nconst wrapInArray = <T>(item: T): T[] => {\n return [item];\n};\n\nconst arr = wrapInArray('test'); // arr: string[]\n\n\nApplication des Generics dans des projets Node.js réels
\n\nGestionnaires d'API avec réponses typées
\n\nImaginez que vous écrivez une fonction universelle pour les requêtes API. Sans génériques, vous perdriez les types de données :
\n\n// Mauvais : any\nasync function fetchData(url: string): Promise<any> {\n const response = await fetch(url);\n return response.json();\n}\n\n// Bon : Generics\nasync function fetchData<T>(url: string): Promise<T> {\n const response = await fetch(url);\n return response.json();\n}\n\n// Utilisation\ninterface User {\n id: number;\n name: string;\n}\n\nconst user = await fetchData<User>('/api/user/1');\nconsole.log(user.name); // TypeScript sait que name est string\n\n\nCollections de données typées
\n\nLorsqu'on travaille avec des bases de données (par exemple via Prisma ou Mongoose), les génériques aident à créer des dépôts universels :
\n\nclass Repository<T extends { id: number }> {\n private items: T[] = [];\n\n add(item: T): void {\n this.items.push(item);\n }\n\n getById(id: number): T | undefined {\n return this.items.find(item => item.id === id);\n }\n}\n\n// Utilisation\ninterface Product {\n id: number;\n title: string;\n price: number;\n}\n\nconst productRepo = new Repository<Product>();\nproductRepo.add({ id: 1, title: 'Laptop', price: 1000 });\nconst laptop = productRepo.getById(1);\n\n\nWrappers pour la gestion des erreurs
\n\nFonction universelle pour