Génériques et types utilitaires
Ce que tu vas apprendre
- Écrire une fonction et un type génériques.
- Contraindre un générique avec
extends. - Dériver de nouveaux types avec les types utilitaires.
Le besoin : une fonction pour tous les types
Imagine une fonction qui renvoie le premier élément d'une liste. Pour des textes, tu écris string[]. Pour des nombres, il faudrait une seconde fonction.
Sans générique, tu es tenté par any :
function premier(liste: any[]): any {
return liste[0]
}
const x = premier([1, 2, 3]) // any : on a perdu le type
Le résultat n'est plus vérifié. Un générique garde le lien entre l'entrée et la sortie.
Un générique, c'est un type en paramètre
Un générique est un type que tu laisses en paramètre, comme tu laisses une valeur en paramètre d'une fonction. On le note entre chevrons.
function premier<T>(liste: T[]): T {
return liste[0]
}
const a = premier(["ail", "sel"]) // string
const b = premier([1, 2, 3]) // number
T est un nom d'usage pour « un type à préciser ». TypeScript le déduit à chaque appel d'après les arguments.
Tu peux aussi le donner toi-même : premier<string>(["ail", "sel"]). En général, l'inférence suffit.
Contraindre un générique
Parfois le type doit avoir une certaine forme. Le mot extends pose une condition.
function nomsDe<T extends { nom: string }>(elements: T[]): string[] {
return elements.map((element) => element.nom)
}
nomsDe([{ nom: "Crêpes", duree: 20 }]) // accepté
nomsDe([{ duree: 20 }]) // Erreur
T peut être n'importe quel type, à condition d'avoir une propriété nom de type texte. Dans la fonction, tu peux donc lire element.nom sans risque.
Un type générique
Les interfaces et les alias acceptent aussi des paramètres de type.
interface Reponse<T> {
donnees: T
erreur: string | null
}
type ReponseRecettes = Reponse<Recette[]>
Une même structure sert à toutes tes réponses d'API : tu précises seulement le contenu. Tu as déjà rencontré cette notation avec Array<string>, équivalent de string[].
Les types utilitaires
TypeScript fournit des types utilitaires : des outils qui transforment un type existant en un autre. Ils évitent de recopier des interfaces entières.
Voici l'interface de départ :
interface Recette {
id: number
nom: string
duree: number
vegetarien: boolean
}
Partial : tout devient optionnel
Partial<Recette> rend toutes les propriétés facultatives. C'est pratique pour une mise à jour partielle.
function modifier(recette: Recette, changements: Partial<Recette>) {
return { ...recette, ...changements }
}
modifier(gratin, { duree: 50 })
Tu n'envoies que les champs qui changent. Required<Recette> fait l'inverse, Readonly<Recette> interdit les modifications.
Pick et Omit : garder ou retirer
Pick conserve les propriétés listées. Omit retire celles que tu indiques.
type Apercu = Pick<Recette, "nom" | "duree">
type NouvelleRecette = Omit<Recette, "id">
Apercu n'a que nom et duree. NouvelleRecette possède tout sauf id, ce qui convient à un formulaire de création, car l'identifiant est attribué après coup.
Les clés sont écrites comme des types littéraux, séparés par |. Une faute de frappe dans un nom est signalée.
Record : un dictionnaire
Record<Cles, Valeur> décrit un objet dont les clés et les valeurs ont les types donnés.
type Difficulte = "facile" | "moyen" | "difficile"
const couleurs: Record<Difficulte, string> = {
facile: "vert",
moyen: "orange",
difficile: "rouge"
}
Si tu oublies une des trois clés, TypeScript le signale. Record<string, number> convient à un objet dont les clés ne sont pas connues d'avance.
Récapitulatif des utilitaires
| Type | Effet |
|---|---|
Partial<T> | Toutes les propriétés deviennent facultatives |
Required<T> | Toutes les propriétés deviennent obligatoires |
Readonly<T> | Toutes les propriétés passent en lecture seule |
Pick<T, K> | Garde seulement les clés K |
Omit<T, K> | Retire les clés K |
Record<K, V> | Crée un objet de clés K et de valeurs V |
Recette change, Apercu et NouvelleRecette suivent sans que tu y touches.À retenir
- Un générique
<T>est un type passé en paramètre, déduit à l'appel. - Il garde le lien entre l'entrée et la sortie, là où
anyle perd. T extends { nom: string }impose une forme minimale.Partial,RequiredetReadonlymodifient toutes les propriétés d'un type.Pickgarde des clés,Omiten retire,Recordfabrique un dictionnaire.- Dériver un type évite de dupliquer ses propriétés.
Pour aller plus loin
Sources officielles
Ensuite
Unions, littéraux et narrowing
Combine plusieurs types avec une union, restreins-les à des valeurs précises, puis affine le type avec typeof, in et les unions discriminées.
TypeScript dans Vue et Nuxt
Utilise TypeScript dans un composant Vue avec script setup, type les props, les événements et les ref, et vérifie les types d'un projet Nuxt.
- Projet
- Lignes de code
- Niveau
- 3e étage
- Appartement
- TypeScript
- Pièce
- 5/7
- Plan n°
- ts/05.generiques-et-utilitaires