Types conditionnels TypeScript
Les types conditionnels sont l'une des fonctionnalités les plus puissantes du système de types de TypeScript.
Ils permettent de sélectionner des types dynamiquement en fonction de conditions, de manière similaire aux expressions ternaires dans les langages de programmation.
Les types conditionnels rendent les définitions de types plus flexibles et constituent la base de la mise en œuvre de types utilitaires avancés.
Diagramme SVG : Flux des types conditionnels
Pourquoi avons-nous besoin des types conditionnels ?
Dans le développement TypeScript réel, nous devons souvent renvoyer différents types selon différents types d'entrée.
Par exemple, une fonction peut accepter des paramètres de type string ou number, et nous devons renvoyer différents types de résultat selon le type du paramètre.
Les types conditionnels offrent la possibilité d'effectuer un jugement logique au niveau des types, ce qui rend les définitions de types plus flexibles et puissantes.
Explication du concept :La syntaxe des types conditionnels est
T extends U ? X : YSi le type T peut être assigné au type U, renvoie le type X ; sinon, renvoie le type Y.
Syntaxe de base
Les types conditionnels utilisent la syntaxe des expressions ternaires pour effectuer des vérifications conditionnelles au niveau des types.
Cela nous permet de calculer dynamiquement le type de retour en fonction du type d'entrée.
Exemple
// Si T est un type chaîne, renvoyer true ; sinon, renvoyer false
type IsString<T> = T extends string ? true : false;
// Utilisation d'un type conditionnel
// string extends string est true, donc le type A est true
type A = IsString<string>;
// number extends string est false, donc le type B est false
type B = IsString<number>;
// Utilisation de ces types
var a: A = true;
var b: B = false;
console.log("Est-ce que string est une chaîne ?: " + a);
console.log("Est-ce que number est une chaîne ?: " + b);
Sortie :
string 是字符串?: true number 是字符串?: false
Explication :Les types conditionnels sont automatiquement évalués lors de la vérification des types pour produire des types concrets.
Application pratique : Filtrage de types
L'une des applications les plus courantes des types conditionnels est le filtrage de types.
Par exemple, vous pouvez créer un type pour exclure null et undefined.
Exemple
// Si T est null ou undefined, renvoyer never (type vide) ; sinon, renvoyer T lui-même
type NonNullable<T> = T extends null | undefined ? never : T;
// Utilisation du type NonNullable
// string n'est pas null/undefined, donc le type est string
type A = NonNullable<string>;
// null est null/undefined, donc le type est never
type B = NonNullable<null>;
// undefined est null/undefined, donc le type est never
type C = NonNullable<undefined>;
// Vérification du type
var a: A = "hello";
console.log("Non nul : " + a);
Sortie :
非空: hello
Le type never :never représente un type qui n'existe jamais. Lorsqu'une condition n'est pas satisfaite, TypeScript utilise never pour représenter un type « indisponible ».
Inférence de type : le mot-clé infer
Le mot-clé infer est l'une des fonctionnalités les plus puissantes des types conditionnels.
Il permet d'extraire ou d'inférer des parties de type spécifiques à partir d'un type.
Exemple
// Si T est un type de fonction, renvoie le type de retour inféré R ; sinon, renvoie never
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// Définir une fonction qui renvoie un objet utilisateur
function getUser() {
return { name: "Alice" };
}
// Définir une fonction qui renvoie un nombre
function getNumber() {
return 42;
}
// Utilisation de ReturnType pour obtenir le type de retour de la fonction
// Inféré comme { name: string }
type R1 = ReturnType<typeof getUser>;
// Inféré comme number
type R2 = ReturnType<typeof getNumber>;
// Utilisation des types inférés
var r1: R1 = { name: "Bob" };
var r2: R2 = 100;
console.log("Utilisateur : " + JSON.stringify(r1));
console.log("Nombre : " + r2);
Sortie :
User: {"name":"Bob"}
数字: 100
Le rôle de infer :infer est comme une "variable" dans le système de types ; il peut capturer une partie spécifique d’un type et l’utiliser dans le type résultant.
Types conditionnels distributifs
Lorsque le paramètre générique d’un type conditionnel est un type union, la "distribution" est automatiquement appliquée.
C’est-à-dire que la condition est appliquée à chaque membre du type union séparément, puis les résultats sont fusionnés.
Exemple
// Lorsque T est un type union, chaque type est automatiquement distribué
type ToArray<T> = T extends any ? T[] : never;
// Les types union sont automatiquement distribués
// string | number est distribué comme : ToArray<string> | ToArray<number>
// c’est-à-dire, string[] | number[]
type StrOrNum = ToArray<string | number>;
// Il peut être assigné à string[] ou number[]
var arr: StrOrNum = ["hello"];
// Il peut aussi recevoir un nombre
arr = 42;
console.log("Array: " + arr);
Sortie :
数组: 42
Mécanisme de distribution :Le comportement distributif des types conditionnels est activé automatiquement. Pour désactiver la distribution, vous pouvez utiliser des crochets :
[T] extends U。
Combinaison des types conditionnels avec les types mappés
Les types conditionnels peuvent être combinés avec des types mappés pour créer de puissants utilitaires de transformation de types.
Cette combinaison est la base pour implémenter les types utilitaires intégrés de TypeScript.
Exemple
interface User {
// ID utilisateur
id: number;
// Nom d'utilisateur
name: string;
// E-mail de l'utilisateur
email: string;
}
// Implémenter Partial en utilisant des types mappés et des types conditionnels
// Parcourir toutes les propriétés de T et ajouter le modificateur optionnel ?
type Partial<T> = {
[P in keyof T]?: T[P];
};
// Implémenter Required à l'aide de types mappés et de types conditionnels
// Parcourir toutes les propriétés de T et supprimer le modificateur optionnel -?
type Required<T> = {
[P in keyof T]-?: T[P];
};
// Utilisation de Partial : toutes les propriétés deviennent optionnelles
var partial: Partial<User> = { name: "Alice" };
// Utilisation de Required : rendre les propriétés de Partial<User> obligatoires
// Le type Partial<User> doit d'abord exister
type RequiredUser = Required<Partial<User>>;
var required: RequiredUser = { name: "Bob", id: 1 };
console.log("Optionnel : " + JSON.stringify(partial));
console.log("Requis : " + JSON.stringify(required));
Sortie :
可选: {"name":"Alice"}
必填: {"name":"Bob","id":1}
Utilisation combinée :La combinaison des types conditionnels et des types mappés peut mettre en œuvre diverses transformations de types complexes, telles que les types utilitaires intégrés comme Partial, Required et Readonly.
Exemple avancé : vérification de type
Les types conditionnels peuvent également être utilisés pour implémenter des vérifications de type plus complexes.
Voici quelques exemples utiles de vérification de type dans un développement réel.
Exemple
// L'intersection de any avec n'importe quel type donne any, donc 0 extends any vaut true
type IsAny<T> = 0 extends (1 & T) ? true : false;
// Test de IsAny
// any est un type spécial ; l'intersecter avec n'importe quel type renvoie any
type A = IsAny<any>; // true
// string n'est pas any
type B = IsAny<string>; // false
console.log("Est-ce que any est any ? : " + A);
console.log("Est-ce que string est any ? : " + B);
// Vérifier si le type peut être assigné
// Renvoie true si T peut être assigné à U, sinon renvoie false
type IsAssignableTo<T, U> = T extends U ? true : false;
// Tester l'assignabilité de type
// string peut être assigné à any, donc renvoie true
type CanAssign = IsAssignableTo<string, any>;
console.log("string peut être assigné à any ? : " + CanAssign);
Résultat de l'exécution:
any 是 any?: true string 是 any?: true
Remarque :any est le type le plus « permissif » de TypeScript. Il peut être assigné à n'importe quel type et peut recevoir des assignations de n'importe quel type.
Notes
- Évaluation paresseuse :Les types conditionnels sont évalués paresseusement ; ils ne sont calculés que lorsque des types concrets sont utilisés.
- Propriété distributive :Les types union déclenchent automatiquement le mécanisme distributif des types conditionnels.
- infer ne peut être utilisé que dans les conditions extends :C'est le seul endroit où infer peut apparaître.
- Combinaison avec les types mappés :Combiner des types conditionnels avec des types mappés peut créer des types utilitaires puissants.
Avancé :De nombreux types utilitaires intégrés de TypeScript (tels que Partial, Required, Extract) sont implémentés à l'aide de types conditionnels.
Résumé
Les types conditionnels sont l'une des fonctionnalités les plus puissantes du système de types de TypeScript.
- Syntaxe de base :
T extends U ? X : Y - Le mot-clé infer :Inférer des parties spécifiques à partir d'un type.
- Propriété distributive :Les types union sont automatiquement distribués.
- Scénarios d'application :Filtrage de types, inférence de types, vérification de types.
- Types utilitaires :La plupart des types utilitaires intégrés sont implémentés à partir de types conditionnels.
Bonnes pratiques :Maîtriser les types conditionnels vous permet d'écrire du code plus flexible et plus sûr du point de vue des types.
Autres extensions