Les génériques en écriture standard nous permettent de passer les types comme une variable aux fonctions et
classes.
Exemple de classes génériques #
Regardons un exemple simplifié à l’extrême, disons que nous voulons avoir un tableau qui peut stocker des éléments de différents types.
stocker des éléments de différents types, commençons par des chaînes de caractères et des nombres :
class NumberArray {
constructor(public arr: number[]) {}
insert(el: number): void {
this.arr.push(el);
}
print(): void {
console.log(this.arr);
}
}
class StringArray {
constructor(public arr: string[]) {}
insert(el: string): void {
this.arr.push(el);
}
print(): void {
console.log(this.arr);
}
}
En regardant l’exemple ci-dessus, nous avons beaucoup de code en double, les seules différences dans l’implémentation de notre code sont les suivantes
différences dans l’implémentation de notre Number
et String
Les classes de tableaux sont
les types associés aux éléments stockés dans les tableaux.
Idéalement, nous voudrions fournir une generic
d’implémentation, en fournissant une
variable pour le type d’éléments que nos tableaux stockent au moment de l’initialisation.
c’est là que les génériques nous aident :
class GenericArray<T> {
constructor(public arr: T[]) {}
insert(el: T): void {
this.arr.push(el);
}
print(): void {
console.log(this.arr);
}
}
const stringArr = new GenericArray<string>(['hello', 'world']);
stringArr.insert('!!!');
stringArr.print();
const numberArr = new GenericArray<number>([1, 2, 3]);
numberArr.insert(4);
numberArr.print();
Nous avons défini une classe GenericArray qui prend un type T
comme argument générique
générique. Lors de l’initialisation de la classe, nous pouvons fournir un type de variable dans le champ
< >
pour spécifier le type de la variable T
dans notre classe doit être remplacé
par.
Dans notre cas, nous avons explicitement spécifié le générique lors de l’initialisation de la classe
mais typescript peut aussi le déduire, de la même manière qu’il déduit les types de retour des fonctions.
des fonctions.
- const stringArr = new GenericArray<string>(['hello', 'world'])
+ const stringArr = new GenericArray(['hello', 'world'])
La façon dont typescript est capable de déduire que T
sera de type string
dans
ce cas, c’est parce que dans notre fonction constructeur, nous avons spécifié que le
que nous transmettons est de type T[]
donc typescript peut regarder les types
des éléments qui composent le tableau passé à la fonction constructeur et en déduire le(s)
le(s) type(s) à partir de là.
La meilleure pratique est d’être explicite sur le type générique, parce que cela nous aide à
d’attraper les erreurs.
Exemple de fonctions génériques #
La syntaxe et le concept d’utilisation des génériques avec les fonctions sont les mêmes :
function returnInArray<T>(value: T): T[] {
return [value];
}
const stringArray = returnInArray<string>('hello world');
const numberArray = returnInArray<number>(123);
Contraintes génériques #
Nous pouvons également appliquer des contraintes autour des génériques pour n’autoriser que certains types à être
passés comme génériques.
class Shark {
swim() {
console.log('The shark swims');
}
}
class Dolphin {
swim() {
console.log('The dolphin swims');
}
}
interface CanSwim {
swim(): void;
}
function callSwim<T extends CanSwim>(obj: T): void {
obj.swim();
}
callSwim<Dolphin>(new Dolphin());
callSwim<Shark>(new Shark());
Dans l’exemple, nous avons une fonction appelée callSwim
qui prend en paramètre un objet
et appelle la fonction swim
sur cet objet. Comme tous les objets n’ont pas un
swim
nous devons utiliser une contrainte avec notre type générique, pour permettre seulement
pour les objets qui confirment la fonction CanSwim
en d’autres termes, implémentent
le site swim
fonction.
Résumé #
Les génériques nous permettent de rendre nos fonctions/classes plus génériques en passant des types
comme paramètres d’appel. Ils sont très similaires aux paramètres de fonction mais
au lieu de valeurs, nous passons des types.