Types d'index TypeScript et le mot-clé keyof

Les types d'index et keyof sont des outils puissants dans TypeScript pour manipuler les types d'objets.

Ils nous permettent d'accéder dynamiquement aux propriétés d'un objet et de créer des mappages de types flexibles.


Diagramme SVG : comment fonctionnent les types d'index et keyof Background Title How Index Types and keyof Work The keyof Operator The keyof Operator type Keys = keyof User // "id" | "name" | "email" Arrow Index Access Types Index Access Types type Value = User["name"] // string Arrow Mapped Types Mapped Types type Partial<T> {[P in keyof T]?: T[P]} Lower half: Use cases Use Cases for Index Types Scenario 1 Dynamic property access Scenario 2 Type-safe object operations Scenario 3 Create generic utility types Arrow marker

Pourquoi avons-nous besoin des types d'index ?

En JavaScript, nous avons souvent besoin d'accéder dynamiquement aux propriétés d'un objet.

Par exemple, une fonction peut avoir besoin d'obtenir toutes les clés d'un objet, ou d'accéder au type de valeur correspondant en fonction d'une clé.

Les types d'index et keyof nous permettent d'exprimer ce dynamisme dans le système de types tout en maintenant la sûreté de typage.

Concept :Les types d'index sont une classe de types qui permettent l'accès dynamique aux propriétés d'un objet, et keyof sert à obtenir un type union composé de toutes les clés d'un type d'objet.


L'opérateur keyof

L'opérateur keyof

Exemple

// Définir le type utilisateur
interface User {
    id: number;       // ID utilisateur
    name: string;     // Nom d'utilisateur
    email: string;    // E-mail
    age?: number;     // Âge (facultatif)
}

// Utiliser keyof pour obtenir le type union de toutes les clés
// Résultat : "id" | "name" | "email" | "age"
type UserKeys = keyof User;

// Test de keyof
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const user: User = {
    id: 1,
    name: "Alice",
    email: "[email protected]"
};

// Récupérer la propriété name
const userName: string = getProperty(user, "name");
console.log("Username: " + userName);

Sortie :

用户名: Alice

Explication :keyof renvoie un type union de littéraux de noms de clés, et non un type string.


Types d'accès indexés

En utilisant les types d'accès indexés, vous pouvez obtenir le type d'une propriété d'objet.

Exemple

// Définir le type utilisateur
interface User {
    id: number;
    name: string;
    email: string;
}

// Utilisez les types d'accès indexés pour obtenir les types de propriétés
type UserId = User["id"];      // number
type UserName = User["name"];  // string

// Vous pouvez utiliser un type union pour accéder à plusieurs clés
type UserIdAndName = User["id" | "name"];  // number | string

// Utilisez keyof pour obtenir l'union de tous les types de propriétés
type AllUserValues = User[keyof User];  // number | string

// Exemple d'utilisation pratique
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const user: User = { id: 1, name: "Bob", email: "[email protected]" };

// Obtenir le type de id
const idValue: number = getValue(user, "id");
console.log("ID: " + idValue);

// Obtenir le type de name
const nameValue: string = getValue(user, "name");
console.log("Name: " + nameValue);

Accès par index :En utilisant des crochets [] vous pouvez accéder au type d'une propriété spécifique d'un type d'objet ; c'est très puissant et flexible.


Notions de base des types mappés

Les types mappés vous permettent de créer de nouveaux types à partir de types existants en itérant sur toutes leurs clés.

Exemple

// Définir le type utilisateur
interface User {
    id: number;
    name: string;
    email: string;
    age: number;
}

// Rendre toutes les propriétés facultatives
type PartialUser = Partial<User>;

// Rendre toutes les propriétés readonly
type ReadonlyUser = Readonly<User>;

// Type mappé personnalisé : rendre toutes les propriétés facultatives et les convertir en chaîne
type Stringify<T> = {
    [P in keyof T]: string;
};

type StringifiedUser = Stringify<User>;

// Exemple d'utilisation pratique
const partialUser: PartialUser = {
    id: 1,
    name: "Alice"
    // email et age sont facultatifs
};

const readonlyUser: ReadonlyUser = {
    id: 1,
    name: "Bob",
    email: "[email protected]",
    age: 25
};
// readonlyUser.name = "Charlie"; // Erreur : readonly

console.log("Utilisateur partiel : " + partialUser.name);
console.log("Utilisateur en lecture seule : " + readonlyUser.name);

Types mappés :L'utilisation de la syntaxe [P in keyof T] pour parcourir toutes les clés d'un type est la base de la création de types utilitaires.


Contraindre le type des clés

Utilisez keyof et des contraintes génériques pour restreindre les clés qu'une fonction accepte.

Exemple

// Définir un type de configuration
interface Config {
    apiUrl: string;
    timeout: number;
    retry: boolean;
}

// Ne peut récupérer que les clés existantes
function getConfigValue<T, K extends keyof T>(
    config: T,
    key: K
): T[K] {
    return config[key];
}

// Définir la configuration
const config: Config = {
    apiUrl: "https://api.example.com",
    timeout: 5000,
    retry: true
};

// Correct : la clé existe
const url: string = getConfigValue(config, "apiUrl");
const timeoutVal: number = getConfigValue(config, "timeout");

// Erreur : la clé n'existe pas (TypeScript signalera une erreur)
// const invalid = getConfigValue(config, "unknown");

console.log("URL de l'API : " + url);
console.log("Délai d'attente : " + timeoutVal);

Contrainte :K extends keyof T garantit que la clé passée doit être une clé qui existe sur l'objet, ce qui évite les erreurs d'exécution.


Obtenir uniquement les propriétés d'un type spécifique

Extraire les clés de propriétés d'un type spécifique à partir d'un type d'objet.

Exemple

// Définir un type mixte
interface Mixed {
    id: number;
    name: string;
    age: number;
    email: string;
    active: boolean;
}

// Extraire toutes les clés de type string
type StringKeys<T> = {
    [K in keyof T]: T[K] extends string ? K : never;
}[keyof T];

// Extraire toutes les clés de type number
type NumberKeys<T> = {
    [K in keyof T]: T[K] extends number ? K : never;
}[keyof T];

// Test
type StringProps = StringKeys<Mixed>;  // "name" | "email"
type NumberProps = NumberKeys<Mixed>;  // "id" | "age"

// Application pratique : obtenir la valeur d'une propriété de type string
function getStringProps<T, K extends StringKeys<T>>(
    obj: T,
    keys: K[]
): T[K][] {
    return keys.map(key => obj[key]);
}

const mixed: Mixed = {
    id: 1,
    name: "Alice",
    age: 25,
    email: "[email protected]",
    active: true
};

const strings = getStringProps(mixed, ["name", "email"]);
console.log("Propriétés des chaînes : " + strings.join(", "));

Types conditionnels :La combinaison des types conditionnels et des types mappés permet un filtrage et une extraction de types complexes.


Itération sur les types de tableaux

Utilisez les types d’index pour manipuler des tableaux et des tuples.

Exemple

// Définir un type tuple
type Tuple = [string, number, boolean];

// Obtenir le type d’élément du tuple
type First = Tuple[0];   // string
type Second = Tuple[1];  // number
type Third = Tuple[2];   // boolean

// Utiliser number pour obtenir le type de tous les éléments
type AllElements = Tuple[number];  // string | number | boolean

// Obtenir le type d'élément de tableau
type StringArray = string[];
type ArrayElement = StringArray[number];  // string

// Application pratique : surcharges de fonctions
function getElement<T extends any[]>(
    arr: T,
    index: number
): T[indexof T] | undefined {
    return index < arr.length ? arr[index] : undefined;
}

const tuple: Tuple = ["hello", 123, true];
const arr: string[] = ["a", "b", "c"];

console.log("Élément de tuple + getElement(tuple, 0));
console.log("Élément de tableau + getElement(arr, 1));

Indexation de tuple :Les tuples peuvent utiliser des indices numériques, et chaque indice correspond à un type d'élément spécifique, ce que les tableaux ne peuvent pas faire.


Notes

  • keyof renvoie un type union :keyof renvoie un type union de littéraux de noms de clés
  • Sécurité de l'accès par index :S'assurer que la clé accédée existe dans le type cible
  • Contraintes génériques :Utiliser extends keyof pour contraindre les paramètres génériques
  • Les types mappés nécessitent le mot-clé in :Les types mappés utilisent la syntaxe [P in keyof T]

Bonnes pratiques :Les types d'index sont la pierre angulaire de la création de types utilitaires génériques ; les maîtriser peut grandement améliorer vos capacités de manipulation de types.


Résumé

Les types d'index et keyof sont des composants importants du système de types de TypeScript.

  • keyof:Obtenir toutes les clés d'un type d'objet
  • Accès par index :Obtenir les types de propriétés via les clés
  • Types mappés :Créer de nouveaux types à partir de types existants
  • Sécurité de type :Assurer la sécurité de type pour l'accès dynamique aux propriétés

Recommandation :Lorsque vous devez manipuler les propriétés d'un objet, privilégiez l'utilisation des types d'index pour maintenir la sécurité de type.

Autres extensions