Indexed access type odczytuje typ właściwości z innego typu, tą samą składnią nawiasów kwadratowych, której JavaScript używa do dostępu do właściwości. Person["age"] to number, a Person["address"]["city"] to string.
Zaletą jest jedno źródło prawdy. Age nie jest drugą kopią number: jeśli Person["age"] zmieni się na string, każdy typ zapisany jako Person["age"] zmieni się razem z nim. Indexed access types istnieją tylko w czasie kompilacji; nie generują żadnego JavaScriptu.
Klucze w unii i T[keyof T]
Indeks może być unią kluczy. Wynikiem jest unia typów właściwości:
Product[keyof Product] to sposób, by na poziomie typów powiedzieć „dowolna wartość właściwości Product”. Pytanie o nieistniejący klucz to błąd kompilacji: Product["price"] daje error TS2339: Property 'price' does not exist on type 'Product'.
Właściwości opcjonalne zawierają undefined
Przy strictNullChecks (włączonym w strict) typ właściwości opcjonalnej zawiera undefined, podobnie jak jej indexed access type:
type Profile = { name: string; nickname?: string };
type Nick = Profile["nickname"]; // string | undefined
Aby pozbyć się undefined, opakuj go: NonNullable<Profile["nickname"]> to string.
Typy elementów tablicy przez T[number]
Tablice są indeksowane liczbami, więc zaindeksowanie typu tablicy przez number daje typ elementu. Działa to też przez kilka poziomów:
Przydaje się to, gdy typ pochodzi z miejsca, nad którym nie masz kontroli, na przykład z wygenerowanego klienta API: ApiResponse["data"]["users"][number] nazywa typ użytkownika, choć nikt go nie wyeksportował.
Krotki: indeks według pozycji
Typ krotki można zaindeksować konkretną pozycją, przez number dla unii typów wszystkich elementów i przez "length" dla jej długości jako typu literalnego:
(typeof arr)[number]: unia z tablicy const
Najczęstsze praktyczne zastosowanie: trzymaj listę dozwolonych wartości jako tablicę (aby móc po niej iterować w czasie wykonania) i wyprowadź z niej typ unii (aby kompilator go sprawdzał).
Działa to dzięki dwóm elementom. as const zamienia tablicę w krotkę tylko do odczytu z typami literalnymi; bez tego ROLES to string[], a (typeof ROLES)[number] to po prostu string. Następnie typeof zamienia wartość w typ, a [number] zbiera jego elementy w unię. Nawiasy są opcjonalne (typeof ROLES[number] znaczy to samo), ale wyjaśniają kolejność operacji.
Sprawdzanie napisu względem tablicy
Ta krotka tylko do odczytu ma jedną niespodziankę. Jej metoda includes przyjmuje tylko typ elementu, więc nie da się przekazać do niej dowolnego napisu:
index.ts(6,25): error TS2345: Argument of type 'string' is not assignable to parameter of type '"admin" | "editor" | "viewer"'.
Na czas sprawdzenia poszerz tablicę do readonly string[] i zamień funkcję w strażnika typu, aby udane sprawdzenie zawężało wartość:
Ten wzorzec daje jedną listę do obu zadań: sprawdzenie w czasie wykonania używa tablicy, a typ jest z niej wyprowadzony, więc nie mogą się rozjechać.
Dostęp indeksowany w generykach: T[K]
Wewnątrz generyka T[K] z K extends keyof T to typ właściwości, którą wybrał wywołujący:
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[]
To ograniczenie K extends keyof T sprawia, że T[K] jest poprawne. Bez niego TypeScript nie może wiedzieć, że K jest kluczem T, i zgłasza error TS2536: Type 'K' cannot be used to index type 'T'.
Składnia w skrócie
| Zapis | Znaczenie | Przykładowy wynik |
|---|---|---|
T["key"] | typ jednej właściwości | Person["age"] to number |
T["a" | "b"] | unia typów tych właściwości | string | number |
T[keyof T] | unia typów wszystkich właściwości | typ każdej wartości |
T["a"]["b"] | zagnieżdżona właściwość | Person["address"]["city"] |
Arr[number] | typ elementu tablicy | User[] daje User |
Tup[0] | element krotki na danej pozycji | typ pierwszego elementu |
Tup["length"] | długość krotki jako literał | 3 |
(typeof arr)[number] | unia wartości tablicy const | "admin" | "editor" |
T[typeof key] | indeks typem stałej | to samo co T["name"] |
Indeks jest zawsze typem. Person[key], gdzie key jest zmienną const, kończy się dwoma błędami: error TS2538: Type 'key' cannot be used as an index type. oraz error TS2749: 'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'?.
Najczęściej zadawane pytania
Czym jest indexed access type w TypeScript?
To typ zapisany jako T[K], który odczytuje typ właściwości K z typu T. Przy type Person = { name: string; age: number } Person["age"] to number. Używa tej samej składni nawiasów kwadratowych co dostęp do właściwości w JavaScripcie, ale działa na typach w czasie kompilacji.
Jak uzyskać typ elementu tablicy w TypeScript?
Zaindeksuj typ tablicy przez number: dla type Users = User[] Users[number] to User. Dla wartości tablicowej połącz to z typeof: (typeof users)[number].
Jak zamienić tablicę napisów w typ unii?
Zadeklaruj tablicę z as const, aby TypeScript zachował wartości literalne, a potem zaindeksuj ją przez number: const roles = ["admin", "editor"] as const; type Role = (typeof roles)[number]; daje "admin" | "editor". Bez as const typem elementu jest po prostu string.
Dlaczego nie mogę użyć zmiennej jako indeksu w indexed access type?
Indeks musi być typem. Person[key] przy const key = "name" kończy się błędami (TS2538 i TS2749), bo key jest wartością. Napisz Person[typeof key] albo użyj literału bezpośrednio: Person["name"].
Co znaczy T[keyof T]?
Indeksuje T unią wszystkich jego kluczy, więc wynikiem jest unia typów wszystkich jego właściwości. Dla { a: string; b: number } to string | number. To sposób, by na poziomie typów powiedzieć „dowolna wartość tego obiektu”.