Génériques
Le couteau suisse du typage - flexibilité sans compromis.
6.1Pourquoi les génériques ?
Sans génériques, tu dois choisir entre la flexibilité (any) et la sécurité :
TypeScripttypescript
// ❌ Pas flexible, une fonction par type
function emballerString(valeur: string): { contenu: string } {
return { contenu: valeur }
}
function emballerNumber(valeur: number): { contenu: number } {
return { contenu: valeur }
}
// ❌ Flexible mais pas sûr - any perd le type
function emballer(valeur: any): { contenu: any } {
return { contenu: valeur }
}
const boite = emballer("hello")
boite.contenu // type: any 😱 - on a perdu l'info
// ✅ Générique : flexible ET sûr
function emballerTypeSafe<T>(valeur: T): { contenu: T } {
return { contenu: valeur }
}
const boiteString = emballerTypeSafe("hello") // { contenu: string }
const boiteNumber = emballerTypeSafe(42) // { contenu: number }
boiteString.contenu.toUpperCase() // ✅ TS sait que c'est un string6.2Fonctions génériques
TypeScripttypescript
// Fonction identité générique
function identite<T>(valeur: T): T {
return valeur
}
identite<string>("hello") // retour: string
identite(42) // retour: number (T inféré)
// Plusieurs paramètres de type
function paire<A, B>(premier: A, second: B): [A, B] {
return [premier, second]
}
const resultat = paire("Alice", 25) // type: [string, number]
// Fonction avec tableau générique
function dernierElement<T>(tableau: T[]): T | undefined {
return tableau[tableau.length - 1]
}
dernierElement([1, 2, 3]) // number | undefined
dernierElement(["a", "b", "c"]) // string | undefined6.3Contraintes génériques (extends)
Parfois, tu veux que le type générique respecte certaines conditions :
TypeScripttypescript
// T doit avoir une propriété "length"
interface AvecLongueur {
length: number
}
function afficherLongueur<T extends AvecLongueur>(element: T): string {
return `Longueur : ${element.length}`
}
afficherLongueur("hello") // ✅ string a .length
afficherLongueur([1, 2, 3]) // ✅ array a .length
// afficherLongueur(42) // ❌ number n'a pas .length
// Contrainte avec keyof
function obtenirPropriete<T, K extends keyof T>(obj: T, cle: K): T[K] {
return obj[cle]
}
const user = { nom: "Alice", age: 25, email: "alice@email.com" }
obtenirPropriete(user, "nom") // ✅ retour: string
obtenirPropriete(user, "age") // ✅ retour: number
// obtenirPropriete(user, "ville") // ❌ "ville" n'est pas une clé de user6.4Interfaces et types génériques
TypeScripttypescript
// Interface générique
interface Reponse<T> {
donnees: T
succes: boolean
message?: string
}
// Utilisation avec différents types
type ReponseUser = Reponse<{ nom: string; email: string }>
type ReponseProduits = Reponse<{ nom: string; prix: number }[]>
const res: ReponseUser = {
donnees: { nom: "Alice", email: "alice@email.com" },
succes: true
}
// Type générique avec valeur par défaut
interface Pagination<T = unknown> {
items: T[]
page: number
total: number
parPage: number
}
const pageUsers: Pagination<Utilisateur> = {
items: [{ nom: "Alice", email: "a@b.com", age: 25, actif: true }],
page: 1,
total: 42,
parPage: 10
}6.5Classes génériques
TypeScripttypescript
// Pile (Stack) générique
class Pile<T> {
private elements: T[] = []
empiler(element: T): void {
this.elements.push(element)
}
depiler(): T | undefined {
return this.elements.pop()
}
sommet(): T | undefined {
return this.elements[this.elements.length - 1]
}
get taille(): number {
return this.elements.length
}
}
// Pile de nombres
const nombres = new Pile<number>()
nombres.empiler(1)
nombres.empiler(2)
nombres.depiler() // 2 (type: number | undefined)
// Pile de strings
const mots = new Pile<string>()
mots.empiler("bonjour")
mots.sommet() // "bonjour" (type: string | undefined)6.6Utility Types natifs
TypeScript fournit des types utilitaires built-in basés sur les génériques. Ce sont tes meilleurs amis :
TypeScripttypescript
interface Utilisateur {
id: number
nom: string
email: string
age: number
actif: boolean
}
// Partial<T> - rend tout optionnel
type UserUpdate = Partial<Utilisateur>
// { id?: number; nom?: string; email?: string; ... }
// Required<T> - rend tout obligatoire
type UserComplet = Required<Utilisateur>
// Readonly<T> - rend tout en lecture seule
type UserFige = Readonly<Utilisateur>
// Pick<T, K> - sélectionne certaines propriétés
type UserPublic = Pick<Utilisateur, "nom" | "age">
// { nom: string; age: number }
// Omit<T, K> - exclut certaines propriétés
type UserSansId = Omit<Utilisateur, "id">
// { nom: string; email: string; age: number; actif: boolean }
// Record<K, T> - crée un objet typé
type Scores = Record<string, number>
const classement: Scores = { alice: 100, bob: 85 }
// ReturnType<T> - extrait le type de retour d'une fonction
function creerUser() { return { id: 1, nom: "Alice" } }
type UserCree = ReturnType<typeof creerUser>
// { id: number; nom: string }