Les Génériques
Problématique
Parfois nous souhaitons pouvoir traiter un type de données sans savoir à l'avance quel type de données il contiendra. C'est le cas notamment des paramètres de fonction.
Supposons que nous voulons une fonciton qui tri un tableau de nombres ou de chaînes de caractères.
Nous allons fonc faire :
function sortNumbers(array: number[]): number[] {
return array.sort((a, b) => a - b);
}
function sortStrings(array: string[]): string[] {
return array.sort();
}
Une solution serait de lister les types possibles.
function sort(array: number[] | string[]): number[] | string[] {
if (typeof array[0] === "number") {
return array.sort((a, b) => a - b);
} else {
return array.sort();
}
}
Mais ce n'est pas très pratique. C'est peu lisible et difficilement maintenable. Notamment si nous voulons ajouter un autre type, les dates.
Les génériques à la rescousse
Les génériques permettent de créer des composants réutilisables qui peuvent fonctionner avec un grand nombre de types différents.
function sort<T>(array: T[]): T[] {
return array.sort();
}
TypeScript comprend le type de array
et l'applique à la fonction sort
.
const numbers = [3.1, 1.2, 2.3];
const sortedNumbers = sort(Math.floor(numbers)); // [1, 2, 3]
const strings = ["c", "a", "b"];
const sortedStrings = sort(strings.toUpperCase); // ["a", "b", "c"]
const dates = [new Date(), new Date(), new Date()];
const sortedDates = sort(dates); // [date1, date2, date3]
La syntaxe est donc
function f<T>(paramètre: T): T {
// Code
}
Vous pouvez mettre n'importe quel nom à la place de T
.
function f<U>(paramètre: U): U {
// Code
}
Etendre
Il est possible d'utiliser un générique, qui se base sur un type.
interface NamedEntity {
name: string;
}
function extractNames<T extends NamedEntity>(items: T[]): string[] {
return items.map(item => item.name);
}
// Utilisation avec un tableau d'objets qui respectent l'interface NamedEntity
const people = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 }
];
const names = extractNames(people);
console.log(names); // Output: ["Alice", "Bob"]
const cities = [
{ "city": "Paris", population: 2200000 },
{ "city": "London", population: 8900000 }
];
const cityNames = extractNames(city); // Erreur de type car city ne contient pas de propriété name