Un type d'accès indexé cherche le type d'une propriété dans un autre type, avec la même syntaxe à crochets que JavaScript utilise pour l'accès aux propriétés. Person["age"] vaut number, et Person["address"]["city"] vaut string.
L'avantage est une source de vérité unique. Age n'est pas une seconde copie de number : si Person["age"] devient string, chaque type écrit Person["age"] change avec lui. Les types d'accès indexé n'existent qu'à la compilation ; ils n'émettent aucun JavaScript.
Unions de clés et T[keyof T]
L'indice peut être une union de clés. Le résultat est l'union des types de propriétés :
Product[keyof Product] est la façon de dire au niveau des types « n'importe quelle valeur de propriété de Product ». Demander une clé qui n'existe pas est une erreur de compilation : Product["price"] donne error TS2339: Property 'price' does not exist on type 'Product'.
Les propriétés optionnelles incluent undefined
Avec strictNullChecks (activé par strict), le type d'une propriété optionnelle inclut undefined, et son type d'accès indexé aussi :
type Profile = { name: string; nickname?: string };
type Nick = Profile["nickname"]; // string | undefined
Pour retirer le undefined, enveloppez-le : NonNullable<Profile["nickname"]> vaut string.
Type des éléments de tableau avec T[number]
Les tableaux sont indexés par des nombres : indexer un type tableau avec number donne donc le type des éléments. Cela fonctionne aussi sur plusieurs niveaux :
C'est utile quand un type vient d'un endroit que vous ne contrôlez pas, comme un client d'API généré : ApiResponse["data"]["users"][number] nomme le type utilisateur sans que personne ne l'ait exporté.
Tuples : indexer par position
Un type tuple peut être indexé par une position précise, par number pour l'union de tous les types d'éléments, et par "length" pour sa longueur sous forme de type littéral :
(typeof arr)[number] : une union à partir d'un tableau const
L'usage le plus courant en pratique : garder une liste de valeurs autorisées sous forme de tableau (pour pouvoir la parcourir à l'exécution) et en dériver le type union (pour que le compilateur la vérifie).
Deux éléments font fonctionner cela. as const fait du tableau un tuple readonly de types littéraux ; sans lui, ROLES est un string[] et (typeof ROLES)[number] n'est qu'un string. Ensuite, typeof transforme la valeur en type, et [number] rassemble ses éléments en une union. Les parenthèses sont facultatives (typeof ROLES[number] signifie la même chose), mais elles rendent l'ordre des opérations clair.
Vérifier une chaîne par rapport au tableau
Ce tuple readonly réserve une surprise. Sa méthode includes n'accepte que le type des éléments : vous ne pouvez donc pas lui passer une chaîne quelconque.
index.ts(6,25): error TS2345: Argument of type 'string' is not assignable to parameter of type '"admin" | "editor" | "viewer"'.
Élargissez le tableau en readonly string[] pour la vérification, et faites de la fonction un type guard pour qu'une vérification réussie affine la valeur :
Ce motif vous donne une seule liste pour les deux rôles : la vérification à l'exécution utilise le tableau, et le type en est dérivé, si bien qu'ils ne peuvent pas diverger.
Accès indexé dans les génériques : T[K]
Dans un générique, T[K] avec K extends keyof T est le type de la propriété choisie par l'appelant, quelle qu'elle soit :
function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
return items.map((item) => item[key]);
}
const users = [
{ name: "Ada", age: 36 },
{ name: "Linus", age: 28 },
];
const names = pluck(users, "name"); // string[]
const ages = pluck(users, "age"); // number[]
C'est la contrainte K extends keyof T qui rend T[K] légal. Sans elle, TypeScript ne peut pas savoir que K est une clé de T et signale error TS2536: Type 'K' cannot be used to index type 'T'.
La syntaxe en un coup d'œil
| Écrit | Signifie | Exemple de résultat |
|---|---|---|
T["key"] | type d'une propriété | Person["age"] vaut number |
T["a" | "b"] | union de ces types de propriétés | string | number |
T[keyof T] | union de tous les types de propriétés | tous les types de valeurs |
T["a"]["b"] | propriété imbriquée | Person["address"]["city"] |
Arr[number] | type des éléments d'un tableau | User[] donne User |
Tup[0] | élément d'un tuple à une position | type du premier élément |
Tup["length"] | longueur d'un tuple en littéral | 3 |
(typeof arr)[number] | union des valeurs d'un tableau const | "admin" | "editor" |
T[typeof key] | indexer avec le type d'une constante | équivaut à T["name"] |
L'indice est toujours un type. Person[key], où key est une variable const, échoue avec deux erreurs : error TS2538: Type 'key' cannot be used as an index type. et error TS2749: 'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'?.
Questions fréquentes
Qu'est-ce qu'un type d'accès indexé en TypeScript ?
Un type écrit T[K] qui cherche le type de la propriété K dans le type T. Avec type Person = { name: string; age: number }, Person["age"] vaut number. Il utilise la même syntaxe à crochets que l'accès aux propriétés en JavaScript, mais agit sur les types, à la compilation.
Comment obtenir le type des éléments d'un tableau en TypeScript ?
Indexez le type tableau avec number : pour type Users = User[], Users[number] vaut User. Pour une valeur tableau, combinez avec typeof : (typeof users)[number].
Comment transformer un tableau de chaînes en type union ?
Déclarez le tableau avec as const pour que TypeScript garde les valeurs littérales, puis indexez-le avec number : const roles = ["admin", "editor"] as const; type Role = (typeof roles)[number]; donne "admin" | "editor". Sans as const, le type des éléments n'est qu'un string.
Pourquoi ne peut-on pas utiliser une variable comme indice dans un type d'accès indexé ?
L'indice doit être un type. Person[key] avec const key = "name" échoue (erreurs TS2538 et TS2749), car key est une valeur. Écrivez Person[typeof key], ou utilisez directement le littéral : Person["name"].
Que signifie T[keyof T] ?
Il indexe T avec l'union de toutes ses clés : le résultat est donc l'union de tous ses types de propriétés. Pour { a: string; b: number }, c'est string | number. C'est la façon de dire au niveau des types « n'importe quelle valeur de cet objet ».