BaliseTonSite

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 string

6.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 | undefined

6.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 user

6.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 }

Verifie tes acquis

3 questions pour valider ce chapitre

1. A quoi servent les generiques (<T>) en TypeScript ?

Valide et sauvegarde ce chapitre

Ne perds pas le fil de ton apprentissage. Chaque QCM terminé sauvegarde ton score. Crée ton profil gratuitement pour débloquer toutes les évaluations du site et retrouver tes résultats plus tard.

Commencer l'aventure
Déjà membre ?Connecte-toi