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