Учитывая интерфейс, мы можем получить доступ к его свойствам определенного типа ключа, например. string:
interface AB {
a: number;
b: boolean;
}
type PropertiesByString = AB[keyof AB & string]
Это работает, потому что keyof AB & string — это "a" | "b", и все эти ключи являются действительными ключами AB. Та же логика неприменима к массивам:
const MyArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
type Person = typeof MyArray[number];
Код действителен, но number здесь включает в себя 4 и многое другое. Почему number здесь разрешено?






Использование number — это особый способ индексации типов массивов.
Тип массива TypeScript (по сравнению с кортежем ) не имеет фиксированной длины. typeof MyArray это просто { name: string; age: number; }[]. Массив переменных времени выполнения может содержать 3 элемента, но на уровне типа об этом нет никакой информации. Теоретически массив может иметь любую длину. Следовательно, индексировать с помощью number часто бывает проще, чем использовать случайные индексы, которые индексируют один и тот же тип.
typeof MyArray[4], typeof MyArray[3] и typeof MyArray[23802302] все действительны. Попробуйте!
Я поддерживаю вашу доброту и то, что я не знал, что typeof MyArray[23802302] не является ошибкой. Но строка «number — это особый способ» и ссылка не могут разрешить мое замешательство по поводу того, «почему это действительно».
Когда ты пишешь
const myArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
TypeScript предполагает, что это тип
// const myArray: { name: string; age: number; }[]
это просто сокращенный способ записи
// const myArray: Array<{ name: string; age: number; }>
Итак, myArray имеет тип массива . Если вы посмотрите на определение типа TypeScript для Array<T> , оно имеет числовую сигнатуру индекса:
interface Array<T> {
// ⋮
[n: number]: T;
}
Это означает, что TypeScript позволит вам индексировать его с помощью числового ключа. В частности, индексированный доступ Array<T>[number] просто вернет вам T.
Кажется, вы спрашиваете, почему он не жалуется, потому что содержимое массива не имеет значений для каждого числового индекса. Ответ в том, что тип не отслеживает содержимое.
Более того, подписи индекса в форме {[key: K]: V} не означают, что «каждый ключ типа K существует в этом объекте и имеет значение типа V». Вместо этого это означает «если в этом объекте существует ключ типа K, то его значение имеет тип V». Таким образом, (typeof myArray)[number] всегда есть { name: string; age: number; }, хотя вы можете ожидать увидеть undefined на практике, если выполните произвольную операцию индексации. Вы можете включить --noUncheckedIndexedAccess, если хотите видеть undefined как возможное значение для операции индексирования:
const person = myArray[4];
// if --noUncheckedIndexedAccess is off
// const person: { name: string; age: number; }
// if --noUncheckedIndexedAccess is on
// const person: { name: string; age: number; } | undefined
но тип индексированного доступа останется прежним.
Существуют типы кортежей , которые ограничивают массивы содержанием определенных типов значений по определенным индексам и длина которых известна заранее. Если вы используете утверждение const в литерале инициализирующего массива, вы получите что-то более близкое к тому типу, который вы предположительно ищете:
const myArrayConst = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
] as const;
/* const myArrayConst: readonly [{
readonly name: "Alice";
readonly age: 15;
}, {
readonly name: "Bob";
readonly age: 23;
}, {
readonly name: "Eve";
readonly age: 38;
}] */
Теперь TypeScript знает, что length — это 3, и будет жаловаться, если увидит, что вы индексируете число, заведомо выходящее за пределы:
let personConst: Person;
personConst = myArrayConst[0]; // okay
personConst = myArrayConst[4]; // error! no element at index '4'
// error! Type 'undefined' is not assignable
Но опять же, это не меняет того, что происходит при цифровой подписи индекса:
type PersonConst = (typeof myArrayConst)[number];
/* type PersonConst = {
readonly name: "Alice";
readonly age: 15;
} | {
readonly name: "Bob";
readonly age: 23;
} | {
readonly name: "Eve";
readonly age: 38;
} */
myArrayConst[Math.random()] // okay, number is still a valid index
Индексирование по-прежнему разрешено, и индексированный доступ по-прежнему дает вам тип элемента без undefined.
Тип
MyArray— этоArray<...>, аArrayимеет индексную сигнатуруnumberв определении типа. Выведенный типABне имеет индексной сигнатуры,numberили чего-то еще, поэтому он не работает. TypeScript больше заботится о типах, чем о фактических значениях. Всегда считается приемлемым индексировать массив с произвольнымnumber, независимо от того, какие данные в нем могут присутствовать. Это полностью решает вопрос? Или я что-то упускаю?