TypeScript - Classes Génériques
Bonjour, futurs superstars du codage ! Aujourd'hui, nous plongeons dans le monde passionnant des classes génériques de TypeScript. Ne vous inquiétez pas si vous êtes nouveau dans la programmation ; je vais vous guider à travers ce voyage étape par étape, comme j'ai fait pour des centaines d'étudiants au fil des ans. Alors, prenez votre boisson favorite, asseyez-vous confortablement, et embarquons dans cette aventure ensemble !
Classes Génériques
Qu'est-ce que les Classes Génériques ?
Imaginez que vous êtes dans une crème glacée, mais au lieu de choisir des saveurs, vous choisissez des types de données. Voilà l'essence des classes génériques ! Elles nous permettent de créer des composants flexibles et réutilisables qui peuvent fonctionner avec différents types de données sans sacrifier la sécurité des types.
Commençons par un exemple simple :
class Box<T> {
private content: T;
constructor(value: T) {
this.content = value;
}
getValue(): T {
return this.content;
}
}
Dans cet exemple, Box
est une classe générique. Le <T>
est comme un placeholder pour un type que nous spécifierons plus tard. C'est comme dire à la crème glacée, "Je déciderai de la saveur lorsque je commanderais !"
Reprenons :
-
class Box<T>
: Cela déclare une classe générique nomméeBox
avec un paramètre de typeT
. -
private content: T
: Nous disons quecontent
sera de typeT
, peu importe ce queT
sera. -
constructor(value: T)
: Le constructeur prend une valeur de typeT
. -
getValue(): T
: Cette méthode retourne une valeur de typeT
.
Maintenant, voyons comment nous pouvons utiliser cette classe :
let numberBox = new Box<number>(42);
console.log(numberBox.getValue()); // Output: 42
let stringBox = new Box<string>("Bonjour, TypeScript !");
console.log(stringBox.getValue()); // Output: Bonjour, TypeScript !
C'est génial, non ? Nous avons utilisé la même classe Box
pour stocker à la fois un nombre et une chaîne de caractères. C'est comme avoir une boîte magique qui peut contenir n'importe quoi que vous mettez dedans, mais se souvient toujours exactement du type de chose qu'elle contient !
Plusieurs Paramètres de Type
Parfois, un seul paramètre de type ne suffit pas. Créons un exemple plus complexe avec plusieurs paramètres de type :
class Pair<T, U> {
private first: T;
private second: U;
constructor(first: T, second: U) {
this.first = first;
this.second = second;
}
getFirst(): T {
return this.first;
}
getSecond(): U {
return this.second;
}
}
Cette classe Pair
peut contenir deux valeurs de types potentiellement différents. C'est comme avoir un cornet de glace à deux boules où chaque boule peut être une saveur différente !
Utilisons notre classe Pair
:
let pair = new Pair<string, number>("Âge", 30);
console.log(pair.getFirst()); // Output: Âge
console.log(pair.getSecond()); // Output: 30
Contraintes Génériques
Parfois, nous voulons limiter les types qui peuvent être utilisés avec notre classe générique. Nous pouvons le faire en utilisant des contraintes. C'est comme dire, "Vous pouvez avoir n'importe quelle saveur de glace, à condition qu'elle ne soit pas trop épicée !"
interface Lengthwise {
length: number;
}
class LengthChecker<T extends Lengthwise> {
checkLength(obj: T): string {
return `La longueur est : ${obj.length}`;
}
}
Dans cet exemple, T extends Lengthwise
signifie que T
doit être un type qui a une propriété length
. Utilisons-le :
let stringChecker = new LengthChecker<string>();
console.log(stringChecker.checkLength("Bonjour")); // Output: La longueur est : 6
let arrayChecker = new LengthChecker<number[]>();
console.log(arrayChecker.checkLength([1, 2, 3])); // Output: La longueur est : 3
// Cela provoquerait une erreur :
// let numberChecker = new LengthChecker<number>();
// Type 'number' does not satisfy the constraint 'Lengthwise'.
Implémentation d'une Interface Générique avec une Classe Générique
Maintenant, levons nos compétences au niveau supérieur en implémentant une interface générique avec une classe générique. C'est comme créer une recette (interface) pour différents types de glace (classes) !
Premièrement, définissons une interface générique :
interface Repository<T> {
getById(id: number): T;
save(item: T): void;
}
Cette interface Repository
définit un contrat pour les classes qui géreront le stockage et la récupération des données. Maintenant, implémentons cette interface avec une classe générique :
class GenericRepository<T> implements Repository<T> {
private items: T[] = [];
getById(id: number): T {
return this.items[id];
}
save(item: T): void {
this.items.push(item);
}
}
Notre classe GenericRepository
implémente l'interface Repository
. Elle peut travailler avec n'importe quel type T
. Utilisons-la :
interface User {
name: string;
age: number;
}
let userRepo = new GenericRepository<User>();
userRepo.save({ name: "Alice", age: 30 });
userRepo.save({ name: "Bob", age: 25 });
console.log(userRepo.getById(0)); // Output: { name: "Alice", age: 30 }
console.log(userRepo.getById(1)); // Output: { name: "Bob", age: 25 }
Dans cet exemple, nous avons créé un dépôt pour des objets User
. Mais la beauté de notre implémentation générique est que nous pourrions tout aussi bien créer un dépôt pour n'importe quel autre type !
Tableau des Méthodes
Voici un tableau pratique résumant les méthodes que nous avons couvertes :
Méthode | Description | Exemple |
---|---|---|
constructor(value: T) |
Crée une nouvelle instance d'une classe générique | new Box<number>(42) |
getValue(): T |
Retourne la valeur stockée dans une classe générique | numberBox.getValue() |
getFirst(): T |
Retourne la première valeur dans une paire | pair.getFirst() |
getSecond(): U |
Retourne la seconde valeur dans une paire | pair.getSecond() |
checkLength(obj: T): string |
Vérifie la longueur d'un objet (avec contraintes) | stringChecker.checkLength("Bonjour") |
getById(id: number): T |
Récupère un élément d'un dépôt par ID | userRepo.getById(0) |
save(item: T): void |
Enregistre un élément dans un dépôt | userRepo.save({ name: "Alice", age: 30 }) |
Et voilà, les amis ! Nous avons parcouru le pays des classes génériques TypeScript, des boîtes simples aux dépôts complexes. Souvenez-vous, la pratique rend parfait, alors n'ayez pas peur d'expérimenter avec ces concepts. Qui sait ? Vous pourriez bien créer la prochaine grande chose en programmation ! Jusqu'à la prochaine fois, bon codage !
Credits: Image by storyset