Types mappés TypeScript

Les types mappés sont une fonctionnalité puissante de TypeScript pour créer de nouveaux types à partir de types existants.

Ils permettent aux développeurs de modifier les caractéristiques des propriétés par lots, comme rendre toutes les propriétés optionnelles ou toutes les propriétés readonly.

Les types mappés sont la technologie centrale pour implémenter les types utilitaires intégrés de TypeScript.


Diagramme SVG : Comment fonctionnent les types mappés Background Title How Mapped Types Work Original Type Original Type interface User { id: number; name: string; } Arrow Mapping syntax Mapping syntax { [P in keyof T]?: T[P] // Add optional modifier } Arrow Result Type Result Type type PartialUser = { id?: number; name?: string; } Lower section: Modifier description Mapped Type Modifiers Modifier Description ? prefix Add optional modifier type Partial<T> readonly prefix Add readonly modifier type Readonly<T> -? prefix Remove optional modifier type Required<T> as keyword Remap key names as `${P}` Arrow marker

Pourquoi avons-nous besoin des types mappés

Dans le développement pratique de TypeScript, nous devons souvent créer des variantes basées sur des types existants.

Par exemple, une version où toutes les propriétés sont optionnelles, ou une version où toutes les propriétés sont readonly.

La méthode traditionnelle consiste à définir ces types manuellement, ce qui est fastidieux et source d'erreurs.

Les types mappés fournissent un moyen déclaratif de générer automatiquement ces variantes de types.

Explication du concept :Les types mappés utilisentkeyofetinle mot-clé pour itérer sur toutes les clés d'un type existant, puis appliquer la même transformation de type à chaque clé.


Types mappés de base

Les types mappés de base créent de nouveaux types en itérant sur toutes les clés du type d'origine.

C'est la base pour implémenter des types utilitaires comme Partial et Readonly.

Exemple

// Définir l'interface utilisateur
interface User {
    // ID utilisateur
    id: number;
    // Nom d'utilisateur
    name: string;
    // E-mail de l'utilisateur
    email: string;
}

// Implémenter le type utilitaire Partial
// Itérer sur toutes les clés de T, ajouter le modificateur optionnel ?
type Partial<T> = {
    // P itère sur toutes les clés renvoyées par keyof T
    // T[P] obtient le type de valeur de la clé correspondante dans le type d'origine
    [P in keyof T]?: T[P];
};

// Utiliser le type Partial
type PartialUser = Partial<User>;

// Le type PartialUser est équivalent à :
// { id?: number; name?: string; email?: string }

// Seules des propriétés partielles peuvent être fournies
var user: PartialUser = { name: "Alice" };

console.log("Utilisateur partiel : " + JSON.stringify(user));

Sortie :

部分用户: {"name":"Alice"}

Explication de la syntaxe :[P in keyof T]Indique l'itération sur toutes les clés du type T.?Le modificateur rend les propriétés optionnelles.


Modificateurs de propriété

Les types mappés prennent en charge plusieurs modificateurs de propriété pour modifier les caractéristiques des propriétés.

Ces modificateurs peuvent être combinés pour répondre à différents besoins de transformation de type.

Exemple

// Définir l'interface utilisateur
interface User {
    // Nom d'utilisateur
    name: string;
    // Âge de l'utilisateur
    age: number;
}

// Utiliser le mappage readonly : rendre toutes les propriétés readonly
// Ajouter le modificateur readonly
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

// Utiliser le mappage optionnel : rendre toutes les propriétés optionnelles (version de base)
type Optional<T> = {
    [P in keyof T]?: T[P];
};

// Utiliser le mappage -? : supprimer le modificateur optionnel (rendre obligatoire)
// -? supprime le modificateur ? d'origine
type Required<T> = {
    [P in keyof T]-?: T[P];
};

// Tester le type readonly
var readonlyUser: Readonly<User> = { name: "Alice", age: 25 };
// readonlyUser.age = 30; // Erreur : une propriété en lecture seule ne peut pas être modifiée

// Tester le type optionnel
var optionalUser: Optional<User> = { name: "Bob" };

console.log("Lecture seule : " + JSON.stringify(readonlyUser));
console.log("Optionnel : " + JSON.stringify(optionalUser));

Sortie :

只读: {"name":"Alice","age":25}
可选: {"name":"Bob"}

Description du modificateur :?ajoute facultatif,-?supprime facultatif ;readonlyajoute readonly,-readonlysupprime readonly.


Remappage des clés

Les types mappés peuvent aussi utiliserasle mot-clé pour remapper les noms de clés.

Ceci est utile lorsque vous devez modifier uniformément les formats des noms de clés.

Exemple

// Définir l'interface utilisateur
interface User {
    // ID utilisateur
    id: number;
    // Nom d'utilisateur
    name: string;
    // Âge de l'utilisateur
    age: number;
}

// Utiliser le mot-clé as pour remapper les noms de clés
// Ajouter un préfixe à toutes les clés
type WithPrefix<T, Prefix extends string> = {
    // Utiliser les types de littéraux de modèle pour renommer les clés
    // Capitalize met la première lettre en majuscule
    [P in keyof T as `${Prefix}${Capitalize<string & P>}`]: T[P];
};

// Utiliser WithPrefix pour ajouter le préfixe "user"
type PrefixedUser = WithPrefix<User, "user">;

// Type converti :
// { userId: number; userName: string; userAge: number }

// Utiliser le type avec préfixe
var user: PrefixedUser = { userId: 1, userName: "Alice", userAge: 25 };

console.log("Avec préfixe : " + JSON.stringify(user));

Sortie :

带前缀: {"userId":1,"userName":"Alice","userAge":25}

Types de littéraux de modèle :Utiliser`${Prefix}${Capitalize}`peuvent générer dynamiquement de nouveaux noms de clés.


Filtrage des clés

En combinant les types conditionnels et les types mappés, le filtrage des clés peut être réalisé.

Ceci est très utile lors de l'implémentation de types utilitaires comme Omit.

Exemple

// Définir l'interface utilisateur
interface User {
    // ID utilisateur
    id: number;
    // Nom d'utilisateur
    name: string;
    // Mot de passe utilisateur
    password: string;
    // E-mail de l'utilisateur
    email: string;
}

// Implémenter Omit : exclure les clés spécifiées
// Utiliser les types conditionnels pour filtrer les clés
type Omit<T, K extends keyof T> = {
    // P parcourt toutes les clés de T
    // Si P est assignable à K (c'est-à-dire, dans la liste d'exclusion), retourner never (exclu)
    // Sinon, retourner P (conserver la clé)
    [P in keyof T as P extends K ? never : P]: T[P];
};

// Utiliser Omit pour exclure la clé password
type UserWithoutPassword = Omit<User, "password">;

// Type converti :
// { id: number; name: string; email: string }

// Utiliser le type après avoir exclu password
var user: UserWithoutPassword = { id: 1, name: "Alice", email: "[email protected]" };

console.log("Sans mot de passe : " + JSON.stringify(user));

Sortie :

无密码: {"id":1,"name":"Alice","email":"[email protected]"}

type never :Dans les types mappés, utiliser never comme type de propriété entraîne la suppression complète de la propriété.


Mappage conditionnel

Les types mappés peuvent être combinés avec des types conditionnels pour appliquer différentes transformations en fonction des types de propriétés.

Cela rend la transformation de type plus flexible et intelligente.

Exemple

// Définir l'interface de réponse d'API
interface APIResponse {
    // Données de réponse
    data: string;
    // Message d'erreur
    error: string;
    // Si en cours de chargement
    isLoading: boolean;
    // Horodatage
    timestamp: number;
}

// Convertir les types de fonction en () => void
// Parcourir toutes les propriétés et effectuer une conversion conditionnelle selon le type de propriété
type FunctionToVoid<T> = {
    // Si T[P] est un type de fonction, le convertir en () => void
    // Sinon, conservez le type d'origine inchangé
    [P in keyof T]: T[P] extends (...args: any[]) => any
        ? () => void
        : T[P];
};

// Utiliser le mappage conditionnel
var response: FunctionToVoid<APIResponse> = {
    data: "hello",
    error: "",
    isLoading: false,
    timestamp: Date.now()
};

console.log("Response: " + JSON.stringify(response));

Sortie :

响应: {"data":"hello","error":"","isLoading":false,"timestamp":...}

Scénarios d'application :Le mappage conditionnel est souvent utilisé dans des scénarios où un traitement différent est nécessaire selon les types, comme le traitement des réponses d'API ou le nettoyage d'objets de configuration.


Types mappés intégrés

TypeScript dispose de nombreux types utilitaires intégrés implémentés à partir de types mappés.

Ces types utilitaires peuvent satisfaire la plupart des besoins quotidiens de développement.

Exemple

// Partial - rend toutes les propriétés optionnelles
type P1 = Partial<{ a: string; b: number }>;
// Résultat : { a?: string; b?: number }

// Required - rend obligatoires toutes les propriétés optionnelles
type R1 = Required<{ a?: string; b?: number }>;
// Résultat : { a: string; b: number }

// Readonly - rend toutes les propriétés readonly
type RO1 = Readonly<{ a: string; b: number }>;
// Résultat : { readonly a: string; readonly b: number }

// Pick - Sélectionne les propriétés spécifiées
type PK = Pick<{ a: string; b: number; c: booléen }, "a" | "b">;
// Résultat : { a: string; b: number }

// Omit - Exclut les propriétés spécifiées
type OM = Omit<{ a: string; b: number; c: booléen }, "c">;
// Résultat : { a: string; b: number }

// Test Partial
console.log("Partial: " + JSON.stringify({} as P1));

// Test Pick
console.log("Pick: " + JSON.stringify({ a: "x" } as PK));

Combinaisons de types utilitaires :Ces types utilitaires intégrés sont tous implémentés à partir de types mappés et de types conditionnels. Comprendre leur fonctionnement peut vous aider à mieux les utiliser.


Notes

  • mot-clé keyof :Utilisé pour obtenir un type union composé de toutes les clés d'un type.
  • mot-clé in :Utilisé pour itérer sur des types union de noms de clés.
  • Position du modificateur :?etreadonlyAvant le nom de la propriété, indique l'ajout d'un modificateur.
  • Modificateur moins :-?et-readonlyUtilisé pour supprimer des modificateurs.
  • mot-clé as :Utilisé pour remapper les noms de clés ; doit renvoyer un type littéral de chaîne ou numérique.

Avancé :Les types mappés peuvent être combinés avec des types conditionnels et des types littéraux de modèle pour implémenter des transformations de type complexes.


Résumé

Les types mappés sont l'une des fonctionnalités les plus puissantes du système de types de TypeScript.

  • keyof:Obtenir toutes les clés d'un type
  • in:Parcourir les noms de clés pour le mappage
  • ?:Ajouter des modificateurs optionnels
  • readonly:Ajouter des modificateurs readonly
  • -?:Supprimer les modificateurs optionnels
  • as:Remapper les noms de clés

Bonnes pratiques :Utiliser judicieusement les types mappés peut considérablement réduire les définitions de type répétitives et améliorer la maintenabilité du code.


Autres extensions