Generics en TypeScript : pourquoi sont-ils nécessaires et comment les utiliser pour les débutants

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : Que sont les Generics et pourquoi sont-ils importants ?

\n\n

Lors de l'apprentissage de TypeScript, vous rencontrez rapidement des situations où vous devez écrire une fonction ou une classe qui peut fonctionner avec différents types de données. Sans outils spéciaux, vous devriez soit dupliquer le code pour chaque type, soit utiliser any, ce qui tue toute la magie du typage strict. C'est là que les Generics (génériques ou généralisations) viennent à la rescousse.

\n\n

Generics est un mécanisme puissant de TypeScript qui permet de créer des composants (fonctions, classes, interfaces) fonctionnant avec différents types, tout en conservant un typage strict. Imaginez que vous écrivez un conteneur universel qui peut stocker à la fois des pommes, des livres et des nombres, mais TypeScript sait exactement ce qui se trouve à l'intérieur dans chaque cas spécifique.

\n\n

Dans cet article, nous allons voir pourquoi les Generics sont nécessaires, comment les déclarer et les utiliser dans des projets réels. Vous apprendrez à rendre votre code plus flexible, plus sûr et plus réutilisable.

\n\n

Bases des Generics : syntaxe et première fonction

\n\n

Commençons par un exemple simple. Supposons que nous ayons besoin d'une fonction qui prend un argument et le retourne (fonction identité). Sans génériques, nous écririons ceci :

\n\n
function identity(arg: number): number {\n    return arg;\n}
\n\n

Mais cette fonction ne fonctionne qu'avec les nombres. Pour la rendre universelle, nous pouvons utiliser any :

\n\n
function identity(arg: any): any {\n    return arg;\n}
\n\n

Le problème est que nous perdons l'information sur le type. TypeScript ne sait pas que le type retourné sera exactement le même que celui passé. C'est là que les Generics interviennent :

\n\n
function identity<T>(arg: T): T {\n    return arg;\n}\n\n// Utilisation :\nlet output1 = identity<string>("Bonjour"); // type string\nlet output2 = identity<number>(42);       // type number
\n\n

Analysons la syntaxe :

\n
    \n
  • <T> — c'est le paramètre de type. Vous pouvez utiliser n'importe quelle lettre, mais par convention on utilise souvent T (Type), U, V.
  • \n
  • arg: T — l'argument est de type T.
  • \n
  • : T après les parenthèses — la fonction retourne une valeur de type T.
  • \n
\n\n

Maintenant, TypeScript sait : si vous avez passé une chaîne, le résultat sera aussi une chaîne. Si un nombre, ce sera un nombre. L'inférence de type fonctionne souvent automatiquement, donc vous pouvez simplement écrire identity("Bonjour") — TypeScript comprendra que T est string.

\n\n

Generics dans les fonctions : travail avec les tableaux et contraintes

\n\n

Le plus souvent, les génériques sont utilisés pour travailler avec des tableaux ou des collections. Considérons une fonction qui retourne le premier élément d'un tableau :

\n\n
function getFirst<T>(arr: T[]): T | undefined {\n    return arr[0];\n}\n\nconst numbers = [1, 2, 3];\nconst firstNumber = getFirst(numbers); // type number\n\nconst strings = ["a", "b", "c"];\nconst firstString = getFirst(strings); // type string
\n\n

Notez : T[] est une notation abrégée pour Array<T>. La fonction fonctionne avec n'importe quel tableau, mais retourne un élément du type exact stocké dans le tableau.

\n\n

Contraintes avec extends

\n\n

Parfois, vous avez besoin que le type T ait certaines propriétés. Par exemple, nous voulons obtenir la longueur d'un objet. Tous les types n'ont pas la propriété length, nous pouvons donc imposer une contrainte :

\n\n
interface HasLength {\n    length: number;\n}\n\nfunction getLength<T extends HasLength>(arg: T): number {\n    return arg.length;\n}\n\ngetLength("chaîne");     // OK, une chaîne a length\ngetLength([1, 2, 3]);    // OK, un tableau a length\ngetLength(42);           // Erreur ! Un nombre n'a pas length

Blogs

Book Recommendations