Menu

Типы доступа по индексу в TypeScript: T[K], T[number]

Тип доступа по индексу читает тип свойства из другого типа: Person["age"] это number. T["key"], объединения ключей, T[keyof T], T[number] для элементов массива, индексы кортежей и (typeof arr)[number], чтобы превратить const-массив в объединение.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Тип доступа по индексу находит тип свойства в другом типе с тем же синтаксисом квадратных скобок, который JavaScript использует для доступа к свойствам. Person["age"] это number, а Person["address"]["city"] это string.

Польза в едином источнике истины. Age это не вторая копия number: если Person["age"] станет string, каждый тип, записанный как Person["age"], изменится вместе с ним. Типы доступа по индексу существуют только при компиляции; они не порождают JavaScript.

Объединения ключей и T[keyof T]

Индекс может быть объединением ключей. Результатом будет объединение типов свойств:

Product[keyof Product] это способ сказать на уровне типов «любое значение свойства Product». Запрос несуществующего ключа это ошибка компиляции: Product["price"] даёт error TS2339: Property 'price' does not exist on type 'Product'.

Необязательные свойства включают undefined

С strictNullChecks (включён при strict) тип необязательного свойства включает undefined, как и его тип доступа по индексу:

type Profile = { name: string; nickname?: string };

type Nick = Profile["nickname"]; // string | undefined

Чтобы убрать undefined, оберните его: NonNullable<Profile["nickname"]> это string.

Типы элементов массива через T[number]

Массивы индексируются числами, поэтому индексация типа массива через number даёт тип элемента. Это работает и через несколько уровней:

Это полезно, когда тип приходит из места, которое вы не контролируете, например из сгенерированного клиента API: ApiResponse["data"]["users"][number] называет тип пользователя, хотя никто его не экспортировал.

Кортежи: индекс по позиции

Тип кортежа можно индексировать конкретной позицией, через number для объединения всех типов элементов и через "length" для его длины в виде литерального типа:

(typeof arr)[number]: объединение из const-массива

Самое частое применение на практике: хранить список допустимых значений в массиве (чтобы по нему можно было пройти во время выполнения) и выводить из него тип объединения (чтобы его проверял компилятор).

Работает это благодаря двум частям. as const делает массив readonly-кортежем литеральных типов; без этого ROLES имеет тип string[], а (typeof ROLES)[number] это просто string. Затем typeof превращает значение в тип, а [number] собирает его элементы в объединение. Скобки необязательны (typeof ROLES[number] означает то же самое), но делают порядок операций понятным.

Проверка строки по массиву

У этого readonly-кортежа есть один сюрприз. Его метод includes принимает только тип элемента, поэтому передать ему произвольную строку нельзя:

index.ts(6,25): error TS2345: Argument of type 'string' is not assignable to parameter of type '"admin" | "editor" | "viewer"'.

Для проверки расширьте массив до readonly string[] и превратите функцию в защитник типа, чтобы успешная проверка сужала значение:

Этот приём даёт один список для обеих задач: проверка во время выполнения использует массив, а тип выводится из него, поэтому они не могут разойтись.

Доступ по индексу в дженериках: T[K]

Внутри дженерика T[K] с K extends keyof T это тип того свойства, которое выбрал вызывающий код:

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[]

Именно ограничение K extends keyof T делает T[K] допустимым. Без него TypeScript не может знать, что K это ключ T, и сообщает error TS2536: Type 'K' cannot be used to index type 'T'.

Синтаксис коротко

ЗаписьЗначениеПример результата
T["key"]тип одного свойстваPerson["age"] это number
T["a" | "b"]объединение типов этих свойствstring | number
T[keyof T]объединение типов всех свойствтип любого значения
T["a"]["b"]вложенное свойствоPerson["address"]["city"]
Arr[number]тип элемента массиваUser[] даёт User
Tup[0]элемент кортежа на позициитип первого элемента
Tup["length"]длина кортежа как литерал3
(typeof arr)[number]объединение значений const-массива"admin" | "editor"
T[typeof key]индекс типом константыто же, что T["name"]

Индекс всегда является типом. Person[key], где key это переменная const, завершается двумя ошибками: error TS2538: Type 'key' cannot be used as an index type. и error TS2749: 'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'?.

Часто задаваемые вопросы

Что такое тип доступа по индексу в TypeScript?

Тип вида T[K], который находит тип свойства K в типе T. С type Person = { name: string; age: number } Person["age"] это number. Он использует тот же синтаксис квадратных скобок, что и доступ к свойствам в JavaScript, но работает с типами при компиляции.

Как получить тип элемента массива в TypeScript?

Проиндексируйте тип массива через number: для type Users = User[] Users[number] это User. Для значения-массива сочетайте это с typeof: (typeof users)[number].

Как превратить массив строк в тип объединения?

Объявите массив с as const, чтобы TypeScript сохранил литеральные значения, затем проиндексируйте его через number: const roles = ["admin", "editor"] as const; type Role = (typeof roles)[number]; даёт "admin" | "editor". Без as const тип элемента будет просто string.

Почему нельзя использовать переменную как индекс в типе доступа по индексу?

Индекс должен быть типом. Person[key] при const key = "name" не работает (ошибки TS2538 и TS2749), потому что key это значение. Напишите Person[typeof key] или используйте литерал напрямую: Person["name"].

Что означает T[keyof T]?

Это индексация T объединением всех его ключей, поэтому результат это объединение всех типов его свойств. Для { a: string; b: number } это string | number. Так на уровне типов говорят «любое значение этого объекта».

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ