TW3 — Technologies du Web 3

Génériques

Écrire du code réutilisable et typer avec les génériques : syntaxe <T>, contraintes extends, cas concrets (Result, ApiResponse).

Les types qu'on a vus sont fixes. Les génériques permettent de paramétrer un type : le code s'adapte au type qu'on lui passe, tout en gardant la sécurité.

🔧 Pourquoi les génériques ?

Sans générique, on devrait écrire une version par type :

// ❌ Sans générique : duplication
function premierString(vals: string[]): string {
  return vals[0];
}
function premierNumber(vals: number[]): number {
  return vals[0];
}
function premierBoolean(vals: boolean[]): boolean {
  return vals[0];
}

Avec un générique, une seule fonction couvre tous les cas :

// ✅ Avec générique : un code, tous les types
function premier<T>(vals: T[]): T {
  return vals[0];
}

const a = premier(["a", "b"]); // type : string
const b = premier([1, 2, 3]); // type : number
const c = premier([true, false]); // type : boolean

📐 Syntaxe de base

Un générique est un type paramétré par une variable (souvent T). À l'appel, T est remplacé par le type réel. C'est l'équivalent d'une fonction, mais pour les types.

Un paramètre de type est la variable T dans <T>. Elle est déduite à l'appel à partir de l'argument. On peut en déclarer plusieurs : <T, U, V>.

Sur une fonction :

function identity<T>(valeur: T): T {
  return valeur;
}

const s = identity("hello"); // string
const n = identity(42); // number

Sur une interface ou un type :

interface Boite<T> {
  contenu: T;
}

const boiteString: Boite<string> = { contenu: "hello" };
const boiteNumber: Boite<number> = { contenu: 42 };

🔒 Contraintes avec extends

La contrainte (extends) restreint les types acceptés par un générique. T extends { nom: string } signifie « T doit avoir au moins un champ nom de type string ».

Sans contrainte, on ne peut rien supposer sur T :

function <>(: ): void {
  .(.nom); // ❌ erreur : on ne connaît pas obj
Property 'nom' does not exist on type 'T'.
}

Avec contrainte, on accède aux propriétés garanties :

function afficherNom<T extends { nom: string }>(obj: T): void {
  console.log(obj.nom); // ✅ OK : T a au moins nom: string
}

afficherNom({ nom: "Alice", age: 25 });
afficherNom({ nom: "Bob" });

🎯 Cas concrets

Le motif Result (générique paramétré)

Le type Result<T> qu'on a vu dans « Interfaces, unions et narrowing » est un générique :

type Result<T, E = string> = { ok: true; valeur: T } | { ok: false; erreur: E };

function diviser(a: number, b: number): Result<number> {
  if (b === 0) return { ok: false, erreur: "Division par zéro" };
  return { ok: true, valeur: a / b };
}

const r = diviser(10, 2);
if (r.ok) {
  console.log(r.valeur); // number
}

Le type ApiResponse

interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

interface User {
  id: number;
  nom: string;
}

interface Post {
  id: number;
  titre: string;
}

async function charger<T>(url: string): Promise<ApiResponse<T>> {
  const res = await fetch(url);
  return res.json() as Promise<ApiResponse<T>>;
}

// Usage : le type est connu à l'appel
const repUser = await charger<User>("/api/users/1");
console.log(repUser.data.nom);

const repPost = await charger<Post>("/api/posts/1");
console.log(repPost.data.titre);

Fonction avec plusieurs génériques

function paire<T, U>(premier: T, second: U): [T, U] {
  return [premier, second];
}

const p1 = paire("Alice", 25); // [string, number]
const p2 = paire(true, "ok"); // [boolean, string]

📋 Points clés

  • Générique (<T>) : paramètre de type qui s'adapte à l'appel.
  • Contrainte (extends) : restreint les types acceptés, permet d'accéder aux propriétés.
  • Déduction : T est souvent déduit automatiquement, pas besoin de l'écrire.
  • Usage : Result<T>, ApiResponse<T>, Boite<T>, fonctions réutilisables.

✅ Vérification des acquis

Quiz du chapitre

3 questions
0 / 3 répondue0%
  1. 1À quoi sert un générique dans une fonction ?
  2. 2Que permet 'extends' sur un générique ?
  3. 3Comment appelle-t-on un type comme 'Promise<string>' ?
Répondez à toutes les questions pour valider.

TypeScript

Terminez le quiz du chapitre pour le marquer comme complété.

Tableau de bord

On this page