Почему в TypeScript допустимо писать `typeof someArray[number]`?

Учитывая интерфейс, мы можем получить доступ к его свойствам определенного типа ключа, например. 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 здесь разрешено?

Тип MyArray — это Array<...>, а Array имеет индексную сигнатуру number в определении типа. Выведенный тип AB не имеет индексной сигнатуры, number или чего-то еще, поэтому он не работает. TypeScript больше заботится о типах, чем о фактических значениях. Всегда считается приемлемым индексировать массив с произвольным number, независимо от того, какие данные в нем могут присутствовать. Это полностью решает вопрос? Или я что-то упускаю?

jcalz 18.04.2024 15:51
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
1
55
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Использование number — это особый способ индексации типов массивов.

Тип массива TypeScript (по сравнению с кортежем ) не имеет фиксированной длины. typeof MyArray это просто { name: string; age: number; }[]. Массив переменных времени выполнения может содержать 3 элемента, но на уровне типа об этом нет никакой информации. Теоретически массив может иметь любую длину. Следовательно, индексировать с помощью number часто бывает проще, чем использовать случайные индексы, которые индексируют один и тот же тип.

typeof MyArray[4], typeof MyArray[3] и typeof MyArray[23802302] все действительны. Попробуйте!

Я поддерживаю вашу доброту и то, что я не знал, что typeof MyArray[23802302] не является ошибкой. Но строка «number — это особый способ» и ссылка не могут разрешить мое замешательство по поводу того, «почему это действительно».

NeoZoom.lua 19.04.2024 08:29
Ответ принят как подходящий

Когда ты пишешь

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.

Ссылка на код детской площадки

Другие вопросы по теме