Menu

Массивы в TypeScript: типы, массив объектов, map и filter

Как типизировать массивы в TypeScript: T[] и Array<T>, массивы объектов, массивы с несколькими типами, readonly-массивы и что возвращают map, filter, reduce и find. Плюс ловушка с includes у литеральных массивов.

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

Тип массива в TypeScript записывается как тип элемента и [] после него: string[] это массив строк, number[] массив чисел. Обобщённая запись Array<string> обозначает тот же тип. Когда у массива есть тип, каждый добавляемый и каждый читаемый элемент имеет этот тип.

Строка с @ts-expect-error это ошибка компиляции (TS2345). Здесь она помечена как ожидаемая, чтобы блок всё равно запустился, а поскольку типы стираются, 42 действительно добавляется в массив во время выполнения: это видно в выводе.

string[] и Array<string>

ЗаписьТо же, чтоПримечания
string[]Array<string>Обычная запись.
(string | number)[]Array<string | number>Скобки обязательны: string | number[] означает «строка или массив чисел».
readonly string[]ReadonlyArray<string>Нет push, pop, sort и присвоения по индексу.
User[]Array<User>Массивы объектов используют тип объекта.
string[][]Array<Array<string>>Двумерный массив (сетка).

Выберите один стиль для всей кодовой базы. Правило array-type в typescript-eslint по умолчанию требует T[].

Массивы объектов

Опишите элемент через псевдоним типа или интерфейс, а затем используйте Type[]. Всё, что читается из массива, проверяется по этой форме.

Объектный литерал, добавляемый в users, должен точно соответствовать User: пропущенное admin или опечатка в имени свойства дают ошибку компиляции.

Типизированные map, filter, reduce и find

Методы массивов обобщённые, поэтому их результаты несут типы. Что каждый метод делает во время выполнения, описано на странице JavaScript методы массивов; TypeScript добавляет к этому типы:

МетодТип результата для T[]
map(fn)U[], где U это то, что возвращает fn
filter(fn)T[] (или более узкий тип, см. ниже)
find(fn)T | undefined
findIndex(fn), indexOf(x)number (-1, если нет)
some(fn), every(fn), includes(x)boolean
reduce(fn, init)тип init (или аргумент типа, reduce<R>(...))
at(i)T | undefined
join(sep)string

Последний пример работает, потому что TypeScript (начиная с 5.5) выводит, что (n) => n !== undefined это предикат типа, и filter возвращает number[], а не (number | undefined)[]. Для проверок, которые он вывести не может, напишите предикат сами: filter((x): x is User => x !== null).

Массивы с несколькими типами

Объединение как тип элемента допускает смесь. Объединение типов массивов не допускает:

Если у позиций фиксированные типы, как в паре [name, age], используйте кортеж: [string, number] знает, что индекс 0 это строка, а индекс 1 число, а (string | number)[] этого не знает.

Readonly-массивы

readonly T[] убирает все методы, которые изменяют массив. Используйте его для параметров, которые функция не должна менять, и для констант.

index.ts(3,12): error TS2339: Property 'push' does not exist on type 'readonly number[]'.

Удалите строку с push, и блок напечатает 4. Изменяемый number[] всегда можно передать туда, где ожидается readonly number[], так что readonly-параметры ничего не стоят вызывающему коду. Проверка работает только во время компиляции: во время выполнения это обычный массив. Чтобы отсортировать readonly-массив, отсортируйте копию: [...values].sort().

Ловушка includes у литеральных массивов

as const превращает массив в readonly-кортеж литеральных типов. Это удобно для списка допустимых значений, но его includes тогда принимает только эти литералы:

Если обернуть проверку в защитник типа (value is Color), расширение происходит один раз, а вызывающий код получает обратно суженное значение.

Индексация и пустые массивы

Чтение arr[i] даёт тип T, даже если i вне диапазона и значение во время выполнения равно undefined. at(i) типизирован как T | undefined, а опция компилятора noUncheckedIndexedAccess заставляет и обычную индексацию возвращать T | undefined.

queue[0].toUpperCase() скомпилировался бы, а затем бросил TypeError во время выполнения. Если индекса может не быть, предпочитайте at(), проверку длины или noUncheckedIndexedAccess.

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

Как объявить тип массива в TypeScript?

Напишите тип элемента и [] после него: let names: string[] = ["a", "b"]. Обобщённая форма Array<string> означает ровно то же самое. Для массива объектов используйте в качестве типа элемента объектный тип или интерфейс: User[].

Чем string[] отличается от Array<string>?

Ничем: это два способа записать один и тот же тип. string[] встречается чаще. Обобщённая форма читается лучше для сложных типов элементов, а для readonly-массивов readonly string[] и ReadonlyArray<string> точно так же совпадают.

Почему find возвращает undefined в TypeScript?

array.find() возвращает T | undefined, потому что подходящего элемента может не найтись. В режиме strict случай undefined нужно обработать проверкой if, опциональной цепочкой (found?.name) или значением по умолчанию (found ?? fallback), прежде чем использовать результат.

Как типизировать массив с несколькими типами в TypeScript?

Используйте объединение в скобках как тип элемента: (string | number)[] это массив, в котором каждый элемент строка или число. Это не то же самое, что string[] | number[]: там либо массив только строк, либо массив только чисел. Для фиксированного порядка типов, например [string, number], используйте кортеж.

Почему includes даёт ошибку на массиве с as const?

У readonly-массива литералов, например ["red", "green"] as const, метод имеет сигнатуру includes(searchElement: "red" | "green"), поэтому передача обычного string даёт ошибку TS2345. Расширьте массив для проверки, (COLORS as readonly string[]).includes(input), лучше всего внутри защитника типа, который сужает input до литерального объединения.

Coddy programming languages illustration

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

НАЧАТЬ