Types Avancés et Utilitaires de TypeScript
Introduction : Au-delà des Fondamentaux
Bienvenue dans cette leçon dédiée aux types avancés et aux utilitaires de types de TypeScript ! Après avoir maîtrisé les bases (types primitifs, interfaces, alias de types, classes, fonctions), il est temps d'explorer la puissance de TypeScript pour écrire du code encore plus robuste, réutilisable et expressif.
TypeScript ne se limite pas à la vérification statique de types ; il offre des outils sophistiqués pour manipuler, transformer et déduire des types à partir d'autres types. Ces capacités, souvent appelées programmation au niveau des types (type-level programming), permettent de créer des abstractions puissantes et d'écrire des bibliothèques génériques qui s'adaptent dynamiquement aux types de données.
Dans cette leçon, nous allons plonger dans les concepts clés qui vous permettront de débloquer le plein potentiel de TypeScript : les opérateurs de types (typeof, keyof), les types d'accès indexés, les types conditionnels (extends, infer), les types mappés, les types littéraux de gabarit, et enfin, la riche collection d'utilitaires de types intégrés. Préparez-vous à transformer votre façon de penser les types !
I. Les Opérateurs de Types Fondamentaux
TypeScript fournit plusieurs opérateurs pour interroger et manipuler les informations de type existantes.
A. L'Opérateur typeof : Inférer des Types à partir de Valeurs
L'opérateur typeof en JavaScript retourne une chaîne de caractères indiquant le type d'une expression au moment de l'exécution (par exemple, "string", "number", "object"). En TypeScript, typeof peut également être utilisé dans le contexte des types pour inférer le type TypeScript d'une variable ou d'une propriété.
C'est particulièrement utile lorsque vous avez une variable dont vous voulez réutiliser le type sans avoir à le redéfinir explicitement.
const monObjet = {
nom: "Alice",
age: 30,
actif: true
};
// Ici, 'TypeMonObjet' sera { nom: string; age: number; actif: boolean; }
type TypeMonObjet = typeof monObjet;
function afficherDetails(obj: TypeMonObjet) {
console.log(`Nom: ${obj.nom}, Âge: ${obj.age}, Actif: ${obj.actif}`);
}
afficherDetails(monObjet);
// Exemple avec une fonction
function creerCompteur() {
let count = 0;
return function increment() {
count++;
return count;
};
}
const compteur = creerCompteur();
// Ici, 'TypeCompteur' sera '() => number'
type TypeCompteur = typeof compteur;
const autreCompteur: TypeCompteur = () => 10; // C'est valide
console.log(autreCompteur());
Explication :
typeof monObjetinfère le type structurel demonObjet. Cela est très pratique pour éviter la duplication de définitions de types.- Notez que
typeofne fonctionne que sur les expressions qui ont un type TypeScript déductible. Il est le plus souvent utilisé sur des variables ou des propriétés.
B. L'Opérateur keyof : Les Clés d'un Objet comme Types
L'opérateur keyof est utilisé pour extraire les noms de propriétés (clés) d'un type d'objet sous forme d'un type union de littéraux de chaînes.
C'est extrêmement utile pour garantir que vous accédez à des propriétés valides d'un objet de manière sûre.
interface Utilisateur {
id: number;
nom: string;
email: string;
estAdmin: boolean;
}
// KeyDeUtilisateur sera 'id' | 'nom' | 'email' | 'estAdmin'
type KeyDeUtilisateur = keyof Utilisateur;
function obtenirPropriete<T, K extends keyof T>(obj: T, cle: K): T[K] {
return obj[cle];
}
const utilisateur1: Utilisateur = {
id: 1,
nom: "Jean Dupont",
email: "jean@example.com",
estAdmin: false
};
const nomUtilisateur = obtenirPropriete(utilisateur1, "nom"); // nomUtilisateur est de type string
console.log(nomUtilisateur);
const idUtilisateur = obtenirPropriete(utilisateur1, "id"); // idUtilisateur est de type number
console.log(idUtilisateur);
// obtenirPropriete(utilisateur1, "adresse"); // Erreur de compilation : 'adresse' n'existe pas sur 'Utilisateur'
Explication :
keyof Utilisateurgénère un type union'id' | 'nom' | 'email' | 'estAdmin'.- La fonction
obtenirProprieteutilise la généricité (T,K) et la contrainteK extends keyof Tpour s'assurer quecleest toujours une clé valide de l'objetobj. - Cela permet une sécurité de type forte lors de l'accès aux propriétés par leur nom.
C. Types d'Accès Indexés (Lookup Types) : Accéder aux Types de Propriétés
Les types d'accès indexés, parfois appelés "Lookup Types", permettent de récupérer le type d'une propriété spécifique d'un autre type en utilisant la notation de crochets, similaire à l'accès aux propriétés d'un objet en JavaScript.
Ils sont souvent utilisés en combinaison avec keyof.
interface Produit {
nom: string;
prix: number;
description: string;
categorie: "electronique" | "alimentaire" | "vetement";
}
// TypeNomProduit sera 'string'
type TypeNomProduit = Produit['nom'];
// TypeCategorieProduit sera 'electronique' | 'alimentaire' | 'vetement'
type TypeCategorieProduit = Produit['categorie'];
// Exemple d'utilisation plus avancée avec des génériques
function extraireValeur<T, K extends keyof T>(objet: T, cle: K): T[K] {
return objet[cle];
}
const livre: Produit = {
nom: "Le Seigneur des Anneaux",
prix: 25.99,
description: "Un classique de la fantasy",
categorie: "vetement" // Erreur, "vetement" n'est pas une catégorie appropriée pour un livre. Oh wait, it should be a product, not a book. But the example is valid for the type.
// Let's fix the example to be more realistic
};
const televiseur: Produit = {
nom: "Téléviseur 4K",
prix: 800,
description: "Un téléviseur de dernière génération.",
categorie: "electronique"
};
const prixTeleviseur: Produit['prix'] = extraireValeur(televiseur, 'prix'); // prixTeleviseur est de type number
console.log(`Prix du téléviseur : ${prixTeleviseur}`);
// On peut aussi accéder au type d'une propriété par une variable de type
type KeyProduit = keyof Produit; // 'nom' | 'prix' | 'description' | 'categorie'
type TypeDuPrix = Produit[KeyProduit]; // string | number | "electronique" | "alimentaire" | "vetement" - C'est une union de TOUS les types de propriétés !
// Utile si on veut un type qui peut être n'importe quelle valeur de propriété de Produit.
Explication :
Produit['nom']vous donne directement le type de la propriéténomde l'interfaceProduit, qui eststring.- C'est un moyen puissant de dériver des types à partir de types existants, sans dupliquer les définitions.
- Lorsque
T[K]est utilisé avec un type génériqueK extends keyof T, comme dansextraireValeur, TypeScript peut précisément inférer le type de retour basé sur la clé fournie.
II. Les Types Conditionnels et l'Inférence
Les types conditionnels permettent de définir un type qui dépend d'une condition. Ils sont un pilier de la programmation au niveau des types en TypeScript.
A. Les Types Conditionnels (T extends U ? X : Y) : Logique Conditionnelle pour les Types
Un type conditionnel prend la forme T extends U ? X : Y. Il exprime une condition sur les relations entre les types :
- Si le type
Test assignable au typeU(c'est-à-dire siTétendU), alors le type résultant estX. - Sinon, le type résultant est
Y.
type EstString<T> = T extends string ? true : false;
type A = EstString<"bonjour">; // A est de type true
type B = EstString<123>; // B est de type false
type C = EstString<string>; // C est de type true
interface Animal {
nom: string;
parler(): string;
}
interface Chien extends Animal {
race: string;
aboie(): string;
}
interface Chat extends Animal {
couleur: string;
miaule(): string;
}
// Si T est un Chien, alors retourne le type de Chien, sinon retourne Animal
type TypeAnimalSpecifique<T> = T extends Chien ? Chien : Animal;
type MonAnimal = TypeAnimalSpecifique<Chien>; // MonAnimal est de type Chien
type AutreAnimal = TypeAnimalSpecifique<Chat>; // AutreAnimal est de type Animal (car Chat n'est pas assignable à Chien)
type AnimalGenerique = TypeAnimalSpecifique<Animal>; // AnimalGenerique est de type Animal
Explication :
- Les types conditionnels apportent une logique
if-elseau système de types de TypeScript. - Ils sont fondamentaux pour créer des utilitaires de types complexes et réutilisables.
B. L'Opérateur infer : Inférer des Types dans les Conditions
L'opérateur infer est utilisé uniquement dans la partie extends d'un type conditionnel. Il permet de "capturer" un type pour l'utiliser dans la partie true ou false de la condition.
C'est incroyablement puissant pour extraire des parties de types complexes, comme le type de retour d'une fonction ou les types des éléments d'un tableau.
// Exemple 1 : Obtenir le type de retour d'une fonction
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type Func1 = () => string;
type Func2 = (a: number, b: string) => boolean[];
type NonFunc = number;
type Ret1 = GetReturnType<Func1>; // Ret1 est de type string
type Ret2 = GetReturnType<Func2>; // Ret2 est de type boolean[]
type Ret3 = GetReturnType<NonFunc>; // Ret3 est de type never (car NonFunc n'est pas une fonction)
// Exemple 2 : Obtenir le type des éléments d'un tableau
type GetArrayElementType<T> = T extends (infer ElementType)[] ? ElementType : T;
type StringArray = string[];
type NumberArray = number[];
type NotAnArray = { a: 1 };
type Elem1 = GetArrayElementType<StringArray>; // Elem1 est de type string
type Elem2 = GetArrayElementType<NumberArray>; // Elem2 est de type number
type Elem3 = GetArrayElementType<NotAnArray>; // Elem3 est de type { a: 1 } (car NotAnArray n'est pas un tableau)
// Exemple 3 : Extraire le type d'une promesse
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type MyPromise = Promise<string>;
type MyPromiseOfNumber = Promise<Promise<number>>; // Promesse imbriquée
type Unpacked1 = UnpackPromise<MyPromise>; // Unpacked1 est de type string
type Unpacked2 = UnpackPromise<MyPromiseOfNumber>; // Unpacked2 est de type Promise<number> (il faut plusieurs niveaux pour déballer entièrement)
// Pour déballer complètement, on utiliserait un type récursif ou l'utilitaire Awaited (vu plus tard).
Explication :
- L'opérateur
infercrée une variable de type temporaire (iciR,ElementType,U) qui n'est disponible que dans le type conditionnel. - Si la condition
T extends ...est remplie, le type deinferest inféré et utilisé. Sinon, la branchefalseest exécutée. inferest l'un des mécanismes les plus avancés et puissants pour la manipulation de types en TypeScript.
III. Les Types Mappés : Transformer des Types Existants
Les types mappés sont un mécanisme puissant pour créer de nouveaux types en transformant les propriétés d'un type existant. Ils itèrent sur chaque propriété d'un type et appliquent une transformation.
A. Les Bases des Types Mappés
La syntaxe d'un type mappé ressemble à une itération for...in en JavaScript : [P in K]: T.
Pest une variable qui représente chaque nom de propriété.Kest un type union de noms de propriétés (souventkeyof T).Test le type que chaque nouvelle propriété aura.
interface UtilisateurConfig {
theme: string;
langue: string;
notifications: boolean;
}
// Rend toutes les propriétés d'un type T optionnelles
type PartialConfig<T> = {
[P in keyof T]?: T[P]; // ? rend la propriété optionnelle
};
// Rend toutes les propriétés d'un type T en lecture seule
type ReadonlyConfig<T> = {
readonly [P in keyof T]: T[P]; // readonly rend la propriété en lecture seule
};
type PartialUtilisateurConfig = PartialConfig<UtilisateurConfig>;
/*
Equivalent à :
type PartialUtilisateurConfig = {
theme?: string;
langue?: string;
notifications?: boolean;
};
*/
type ReadonlyUtilisateurConfig = ReadonlyConfig<UtilisateurConfig>;
/*
Equivalent à :
type ReadonlyUtilisateurConfig = {
readonly theme: string;
readonly langue: string;
readonly notifications: boolean;
};
*/
const config: PartialUtilisateurConfig = {
theme: "dark"
}; // OK, langue et notifications sont optionnelles
const readonlyConfig: ReadonlyUtilisateurConfig = {
theme: "light",
langue: "fr",
notifications: true
};
// readonlyConfig.theme = "blue"; // Erreur : Cannot assign to 'theme' because it is a read-only property.
B. Modificateurs de Propriétés (readonly, ?)
Les types mappés permettent d'ajouter ou de supprimer des modificateurs de propriétés comme readonly et ? (optionnel) en utilisant des préfixes + ou -.
+readonly/-readonly: Ajoute ou supprime le modificateurreadonly.+?/-?: Ajoute ou supprime le modificateur?(optionnel).
Si vous ne mettez pas de + ou -, l'opérateur est implicitement ajouté. Par exemple, [P in keyof T]: T[P] est équivalent à [P in keyof T]: +T[P] (dans le sens où il n'y a pas de ? ni readonly s'ils n'étaient pas déjà là ou s'ils sont ajoutés explicitement).
interface Point {
x: number;
readonly y: number; // y est en lecture seule
z?: number; // z est optionnelle
}
// Rendre toutes les propriétés obligatoires et modifiables
type MutableAndRequired<T> = {
-readonly [P in keyof T]-?: T[P];
};
type FullPoint = MutableAndRequired<Point>;
/*
Equivalent à :
type FullPoint = {
x: number;
y: number; // y n'est plus readonly
z: number; // z n'est plus optionnel
};
*/
const fullPoint: FullPoint = { x: 10, y: 20, z: 30 };
fullPoint.y = 25; // OK, y est maintenant modifiable
fullPoint.z = 35; // OK, z est maintenant obligatoire et doit être présent à l'initialisation si non optionnel
Explication :
- Les types mappés sont à la base de nombreux utilitaires de types intégrés (comme
Partial,Readonly,Requiredque nous verrons plus bas). - Ils permettent des transformations de types complexes et génériques, rendant le code plus DRY (Don't Repeat Yourself).
IV. Les Types Littéraux de Gabarit (Template Literal Types) : Manipulation de Chaînes au Niveau des Types
Introduits avec TypeScript 4.1, les types littéraux de gabarit permettent de construire de nouveaux types de chaînes en utilisant la syntaxe des template literals de JavaScript. Ils peuvent inférer des littéraux de chaînes et les manipuler, ajoutant une capacité de programmation au niveau des types pour les chaînes.
Ils peuvent utiliser des unions de types, typeof, keyof et même infer.
type CardinalDirection = "North" | "East" | "South" | "West";
type OrdinalDirection = "NorthEast" | "SouthEast" | "SouthWest" | "NorthWest";
// Concaténer des chaînes de manière typée
type AllDirections = `${CardinalDirection} | ${OrdinalDirection}`;
// AllDirections sera "North | NorthEast" | "North | SouthEast" | ... | "West | NorthWest" (une union de toutes les combinaisons)
// Utiliser des inférences pour manipuler des chaînes
type CssProperty = "margin-top" | "padding-bottom" | "font-size";
// Pour transformer "margin-top" en "marginTop" (camelCase)
type KebabToCamelCase<S extends string> = S extends `${infer First}-${infer Rest}`
? `${First}${Capitalize<KebabToCamelCase<Rest>>}`
: S;
type MyCamelCaseProp = KebabToCamelCase<CssProperty>;
// MyCamelCaseProp sera "marginTop" | "paddingBottom" | "fontSize"
// Créer des types d'événements spécifiques
type EventName<T extends string> = `${T}Changed` | `${T}Added` | `${T}Removed`;
type UserEvents = EventName<"user">;
// UserEvents sera "userChanged" | "userAdded" | "userRemoved"
type ProductEvents = EventName<"product">;
// ProductEvents sera "productChanged" | "productAdded" | "productRemoved"
Explication :
- Ils utilisent le même principe que les template literals JavaScript, mais appliqués aux types.
- Des utilitaires de type intégrés comme
Capitalize<S>,Uncapitalize<S>,Uppercase<S>,Lowercase<S>sont souvent utilisés avec les types littéraux de gabarit pour manipuler la casse des chaînes. - Ces types sont incroyablement utiles pour générer des types basés sur des conventions de nommage (par exemple, des noms de colonnes SQL, des noms d'événements, des noms de fichiers).
V. Les Utilitaires de Types Prédéfinis (Built-in Utility Types)
TypeScript inclut une série d'utilitaires de types prédéfinis qui couvrent les cas d'usage courants de transformation de types. Ils sont implémentés en utilisant les concepts que nous venons de voir (types mappés, types conditionnels, keyof, infer, etc.). Les connaître vous fera gagner un temps précieux et rendra votre code plus idiomatique.
A. Manipulation de Propriétés
1. Partial<T> : Rendre Toutes les Propriétés Optionnelles
Construit un type avec toutes les propriétés de T définies comme optionnelles. C'est l'un des plus utilisés.
interface ParametresRecherche {
query: string;
page: number;
sortBy: string;
ascending: boolean;
}
type OptionsRecherche = Partial<ParametresRecherche>;
/*
Equivalent à :
type OptionsRecherche = {
query?: string;
page?: number;
sortBy?: string;
ascending?: boolean;
};
*/
const options: OptionsRecherche = {
query: "typescript",
page: 1
}; // OK, les autres propriétés sont optionnelles.
2. Required<T> : Rendre Toutes les Propriétés Obligatoires
Construit un type avec toutes les propriétés de T définies comme obligatoires. L'inverse de Partial.
type IncompleteUser = {
id: number;
name?: string;
email?: string;
};
type CompleteUser = Required<IncompleteUser>;
/*
Equivalent à :
type CompleteUser = {
id: number;
name: string;
email: string;
};
*/
const user: CompleteUser = {
id: 1,
name: "Jane Doe",
email: "jane@example.com"
}; // Toutes les propriétés sont maintenant obligatoires.
// const incompleteUser: CompleteUser = { id: 2 }; // Erreur : Property 'name' is missing...
3. Readonly<T> : Rendre Toutes les Propriétés en Lecture Seule
Construit un type avec toutes les propriétés de T définies comme readonly.
interface CompteBancaire {
numero: string;
solde: number;
titulaire: string;
}
type CompteImmuable = Readonly<CompteBancaire>;
/*
Equivalent à :
type CompteImmuable = {
readonly numero: string;
readonly solde: number;
readonly titulaire: string;
};
*/
const monCompte: CompteImmuable = {
numero: "FR12345",
solde: 1000,
titulaire: "Alice"
};
// monCompte.solde = 1200; // Erreur : Cannot assign to 'solde' because it is a read-only property.
4. Record<K, T> : Construire un Type d'Objet avec des Clés Spécifiques
Construit un type d'objet dont les clés sont de type K et les valeurs sont de type T. Utile pour les dictionnaires ou les mappages.
type Page = "home" | "about" | "contact";
interface PageData {
title: string;
content: string;
}
type SiteMap = Record<Page, PageData>;
/*
Equivalent à :
type SiteMap = {
home: PageData;
about: PageData;
contact: PageData;
};
*/
const sitemap: SiteMap = {
home: { title: "Accueil", content: "Bienvenue sur notre site." },
about: { title: "À propos", content: "En savoir plus sur nous." },
contact: { title: "Contact", content: "Contactez-nous." }
};
console.log(sitemap.home.title);
5. Pick<T, K> : Sélectionner un Sous-ensemble de Propriétés
Construit un type en sélectionnant un ensemble de propriétés K d'un type T. K doit être une union de littéraux de chaînes correspondant à des clés de T.
interface Produit {
id: number;
nom: string;
prix: number;
description: string;
stock: number;
}
type ApercuProduit = Pick<Produit, "nom" | "prix">;
/*
Equivalent à :
type ApercuProduit = {
nom: string;
prix: number;
};
*/
const monProduit: ApercuProduit = {
nom: "Clavier mécanique",
prix: 120.99
};
// monProduit.id; // Erreur : Property 'id' does not exist on type 'ApercuProduit'.
6. Omit<T, K> : Exclure un Sous-ensemble de Propriétés
Construit un type en prenant toutes les propriétés d'un type T sauf celles spécifiées par K. L'inverse de Pick.
interface Tache {
id: number;
titre: string;
description: string;
completee: boolean;
dateCreation: Date;
}
type NouvelleTache = Omit<Tache, "id" | "dateCreation" | "completee">;
/*
Equivalent à :
type NouvelleTache = {
titre: string;
description: string;
};
*/
const tacheADefinir: NouvelleTache = {
titre: "Apprendre TypeScript",
description: "Maîtriser les types avancés et les utilitaires."
};
// tacheADefinir.id; // Erreur
B. Transformation et Filtrage de Types
1. Exclude<T, U> : Exclure des Types d'un Ensemble
Construit un type en excluant de T tous les types qui sont assignables à U.
type PossibleStatus = "pending" | "success" | "error" | null | undefined;
// Supprime 'null' et 'undefined'
type ValidStatus = Exclude<PossibleStatus, null | undefined>; // "pending" | "success" | "error"
type MixedUnion = string | number | boolean | symbol;
// Supprime les types non numériques
type NonStringTypes = Exclude<MixedUnion, string | boolean>; // number | symbol
2. Extract<T, U> : Extraire des Types Communs d'un Ensemble
Construit un type en extrayant de T tous les types qui sont assignables à U. L'inverse de Exclude.
type AllHTTPMethods = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
// Extraire les méthodes qui peuvent modifier des données
type MutatingMethods = Extract<AllHTTPMethods, "POST" | "PUT" | "DELETE" | "PATCH">; // "POST" | "PUT" | "DELETE" | "PATCH"
type NumericOrBoolean = number | boolean;
type NumericTypes = Extract<MixedUnion, NumericOrBoolean>; // number | boolean
3. NonNullable<T> : Supprimer null et undefined
Construit un type en excluant null et undefined de T.
type PeutEtreNullOuUndefined = string | number | null | undefined;
type CertainementPresent = NonNullable<PeutEtreNullOuUndefined>; // string | number
let valeur: CertainementPresent = "hello";
valeur = 123;
// valeur = null; // Erreur
C. Types Liés aux Fonctions
1. Parameters<T> : Obtenir les Types des Paramètres d'une Fonction
Construit un type tuple des types de paramètres d'une fonction T.
function calculerSomme(a: number, b: number): number {
return a + b;
}
function afficherMessage(message: string, count: number, prefix?: string): void {
console.log(`${prefix || ''} ${message} (${count})`);
}
type SommeParams = Parameters<typeof calculerSomme>; // [a: number, b: number]
type MessageParams = Parameters<typeof afficherMessage>; // [message: string, count: number, prefix?: string | undefined]
// Utilisation
const paramsSomme: SommeParams = [10, 20];
const resultat = calculerSomme(...paramsSomme); // 30
const paramsMessage: MessageParams = ["Bonjour", 5, "TS:"];
afficherMessage(...paramsMessage); // TS: Bonjour (5)
2. ReturnType<T> : Obtenir le Type de Retour d'une Fonction
Construit un type qui est le type de retour d'une fonction T.
type SommeResult = ReturnType<typeof calculerSomme>; // number
type MessageResult = ReturnType<typeof afficherMessage>; // void
async function fetchData(): Promise<string> {
return "Data fetched!";
}
type DataResult = ReturnType<typeof fetchData>; // Promise<string> (notez que c'est le type de retour de la fonction, pas le type déballé de la promesse)
3. ThisParameterType<T>, OmitThisParameter<T>, ThisType<T>
Ces utilitaires sont plus avancés et concernent la gestion du type this dans les fonctions.
ThisParameterType<T>: Extrait le type du paramètrethisd'une fonction.OmitThisParameter<T>: Supprime le paramètrethisd'une fonction.ThisType<T>: Marqueur contextuel pour définir le type dethisdans les littéraux d'objet (utile pour lesmixinsou les bibliothèques).
Nous ne les détaillerons pas ici, mais sachez qu'ils existent pour des scénarios de type this plus complexes.
D. Types Asynchrones
1. Awaited<T> : Obtenir le Type Déballé d'une Promesse
Introduit avec TypeScript 4.5, cet utilitaire est conçu pour représenter le type obtenu après avoir attendu la résolution d'une promesse (ou de plusieurs promesses imbriquées).
type PromiseType1 = Promise<string>;
type PromiseType2 = Promise<Promise<number>>; // Promesse imbriquée
type PromiseType3 = Promise<string | Promise<boolean>>; // Union
type Result1 = Awaited<PromiseType1>; // Result1 est de type string
type Result2 = Awaited<PromiseType2>; // Result2 est de type number
type Result3 = Awaited<PromiseType3>; // Result3 est de type string | boolean
async function obtenirDonnees(): Promise<{ nom: string }> {
return { nom: "Alice" };
}
type TypeDonnees = Awaited<ReturnType<typeof obtenirDonnees>>; // { nom: string }
Explication :
Awaited"déballe" récursivement le type d'une promesse jusqu'à atteindre sa valeur finale.- C'est l'équivalent typé de l'opérateur
awaiten JavaScript.
Conclusion : Maîtriser la Flexibilité de TypeScript
Félicitations ! Vous avez parcouru un chemin dense mais extrêmement enrichissant à travers les types avancés et les utilitaires de TypeScript.
Nous avons exploré :
- Les opérateurs
typeofetkeyofpour inférer et manipuler les clés et types de valeurs. - Les types d'accès indexés pour extraire des types de propriétés spécifiques.
- Les puissants types conditionnels et l'opérateur
inferpour construire des logiques de types complexes. - Les types mappés pour transformer des structures de types entières.
- Les types littéraux de gabarit pour manipuler des chaînes au niveau des types.
- Une panoplie d'utilitaires de types prédéfinis (
Partial,Required,Pick,Omit,Exclude,Extract,NonNullable,Parameters,ReturnType,Awaited) qui sont des implémentations de ces concepts avancés.
La maîtrise de ces outils vous permettra d'écrire des applications TypeScript non seulement robustes et scalables, mais aussi incroyablement flexibles et adaptables. Vous serez en mesure de :
- Créer des bibliothèques plus génériques.
- Réduire la duplication de code et de types.
- Améliorer la sécurité des types dans des scénarios complexes.
- Mieux comprendre le code TypeScript avancé que vous rencontrerez.
N'oubliez pas que la meilleure façon d'assimiler ces concepts est de les pratiquer. Expérimentez avec ces utilitaires et essayez de créer vos propres types utilitaires pour résoudre des problèmes spécifiques. Le monde des types est vaste et fascinant ; continuez à explorer !