Les génériques dans TypeScript

Les génériques dans TypeScript

Cet article explique les génériques dans TypeScript.

YouTube Video

Les génériques dans TypeScript

Les génériques dans TypeScript sont une fonctionnalité qui permet de définir des fonctions, des classes et des interfaces réutilisables et sûres, en paramétrant les types. L'utilisation des génériques permet d'écrire du code qui ne dépend pas de types spécifiques, permettant ainsi d'effectuer les mêmes opérations sur différents types.

Bases des génériques

Les génériques agissent comme des modèles qui acceptent des types en tant qu'arguments, permettant aux fonctions et aux classes de gérer différents types.

Fonctions génériques

Voici un exemple de fonction avec des types d'arguments spécifiés à l'aide de génériques.

1function identity<T>(value: T): T {
2    return value;
3}
4
5console.log(identity<number>(42));       // 42
6console.log(identity<string>("Hello"));  // Hello
  • T est un argument de type générique qui représente les types des arguments et de la valeur de retour de la fonction. Le type réel est déterminé au moment de l'appel de la fonction.
  • En spécifiant explicitement <number> ou <string>, vous définissez le type.

Les types génériques fonctionnent sans spécification explicite car TypeScript effectue une inférence de type.

1function identity<T>(value: T): T {
2    return value;
3}
4
5console.log(identity(42));       // 42
6console.log(identity("Hello"));  // Hello

Contraintes sur les génériques

En imposant des contraintes sur les génériques, vous pouvez les restreindre à n'accepter que des types spécifiques.

1function loggingIdentity<T extends { length: number }>(arg: T): T {
2    console.log(arg.length);
3    return arg;
4}
5
6loggingIdentity("Hello");  // 5
7loggingIdentity([1, 2, 3]);  // 3
8
9// loggingIdentity(42);  // Error: number does not have a length property.
  • En spécifiant T extends { length: number }, vous indiquez que T doit être un type ayant une propriété length. Ainsi, les types sans propriété length ne seront pas acceptés.

Classes génériques

Les classes peuvent également être définies à l'aide de génériques. Les classes génériques offrent des types flexibles pour les propriétés et les méthodes.

 1class Box<T> {
 2    private _value: T;
 3
 4    constructor(value: T) {
 5        this._value = value;
 6    }
 7
 8    public getValue(): T {
 9        return this._value;
10    }
11
12    public setValue(value: T): void {
13        this._value = value;
14    }
15}
16
17const numberBox = new Box<number>(100);
18console.log(numberBox.getValue());  // 100
19
20const stringBox = new Box<string>("Hello");
21console.log(stringBox.getValue());  // Hello
  • Box<T> déclare le type T utilisé dans la classe comme générique. Cela permet de réutiliser la même classe pour différents types.

Interfaces génériques

Les génériques peuvent également être utilisés avec des interfaces.

 1interface Pair<T, U> {
 2    first: T;
 3    second: U;
 4}
 5
 6const numberStringPair: Pair<number, string> = { first: 1, second: "One" };
 7console.log(numberStringPair);  // { first: 1, second: 'One' }
 8
 9const booleanArrayPair: Pair<boolean, number[]> = { first: true, second: [1, 2, 3] };
10console.log(booleanArrayPair);  // { first: true, second: [ 1, 2, 3 ] }
  • En spécifiant deux types génériques avec Pair<T, U>, vous pouvez définir un objet combinant différents types.

Arguments de type par défaut

Il est également possible de spécifier un type par défaut pour les arguments de type générique.

1function createArray<T = string>(length: number, value: T): T[] {
2    return Array(length).fill(value);
3}
4
5console.log(createArray(3, "a"));   // ['a', 'a', 'a']
6console.log(createArray(3, 100));   // [100, 100, 100]
  • Nous définissons l'argument de type par défaut comme string avec <T = string>. Si aucun type n'est spécifié explicitement, T sera de type string.

Alias de type générique

Les génériques peuvent également être utilisés comme alias de type (type).

 1type Result<T> = {
 2    success: boolean;
 3    data: T;
 4};
 5
 6const successResult: Result<number> = { success: true, data: 42 };
 7const errorResult: Result<string> = { success: false, data: "Error occurred" };
 8
 9console.log(successResult);  // { success: true, data: 42 }
10console.log(errorResult);    // { success: false, data: 'Error occurred' }
  • Result<T> représente un objet de résultat contenant des données de type T. De cette manière, vous pouvez créer des alias de type flexibles à l'aide de génériques.

Types génériques multiples

En utilisant plusieurs types génériques, vous pouvez définir des fonctions et des classes encore plus polyvalentes.

1function merge<T, U>(obj1: T, obj2: U): T & U {
2    return { ...obj1, ...obj2 };
3}
4
5const person = { name: "Alice" };
6const job = { title: "Engineer" };
7
8const merged = merge(person, job);
9console.log(merged);  // { name: 'Alice', title: 'Engineer' }
  • La fonction merge prend deux types différents T et U et les combine pour retourner un nouvel objet.

Résumé

  • Les génériques permettent d'écrire du code réutilisable et sûr en manipulant les types comme des paramètres.
  • En utilisant des génériques dans les fonctions, classes et interfaces, vous pouvez écrire une logique flexible qui gère divers types.
  • En ajoutant des contraintes aux arguments de type ou en définissant des arguments de type par défaut, vous pouvez contrôler la portée des génériques.

En utilisant des génériques, vous pouvez écrire du code indépendant des types et polyvalent, en exploitant au maximum le puissant système de types de TypeScript.

Vous pouvez suivre l'article ci-dessus avec Visual Studio Code sur notre chaîne YouTube. Veuillez également consulter la chaîne YouTube.

YouTube Video