Типы массивов
Чему вы научитесь:
- записывать тип массива как
number[]и читать тип, который TypeScript вывел сам; - запрещать изменения массива типом
readonly number[]; - описывать кортеж — массив, где у каждого места свой тип, например
[string, number]; - сохранять в типе точные значения массива и объекта через
as const.
![Ряд одинаковых шкафчиков number[] и карточка с двумя ячейками [0] и [1]](/course-covers/typescript-beginners/06-arrays-tuples.webp)
Представьте ряд шкафчиков в камере хранения. Над рядом висит табличка «Только для чемоданов». Шкафчики пронумерованы с нуля, как элементы массива, и в каждом лежит одно и то же — чемодан. Служащий, который открывает любой из них, заранее знает, что достанет.
В JavaScript у массива такой таблички нет. В него можно положить что угодно, и ошибка всплывёт позже, например при подсчёте. Вот программа, которая складывает баллы:
const scores = [90, 75];
scores.push("100"); // ✗
let total = 0;
for (const s of scores) {
total += s;
}
console.log(total);error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.tsc сообщает: аргумент типа string нельзя передать в параметр типа number. TypeScript посмотрел на литерал [90, 75] и повесил над массивом табличку «только числа», поэтому строка "100" в push не прошла.
Если бы этот JavaScript запустили, программа вывела бы 165100. К числу 165 прибавилась строка "100", и оператор + склеил их в одну строку. TypeScript находит такую ошибку на строке с push, а не там, где всплыл странный результат.
Тип «массив чисел»
Тип массива записывают как тип элемента и пару квадратных скобок: number[] читается «массив чисел», string[] — «массив строк». Это и есть табличка над рядом шкафчиков: она говорит, что лежит в каждом элементе.
Для const scores = [90, 75] TypeScript вывел тип number[] сам, и подсказка редактора покажет const scores: number[]. Писать тип вручную нужно там, где выводить его не из чего, например у параметра функции:
function average(nums: number[]): number {
let sum = 0;
for (const n of nums) {
sum += n;
}
return sum / nums.length;
}
console.log(average([90, 75, 60])); // 75Давайте разберём пример по строкам:
nums: number[]— функция принимает массив чисел, и вызов с массивом строк не скомпилируется.- В цикле
for…ofуnтипnumber: TypeScript берёт его с таблички массива. sum / nums.length— число, поэтому результат совпадает с аннотациейnumber.- Программа печатает
75.
Тип массива проверяет каждое место, где массив получает элемент или отдаёт его:
scores.push(x)принимает толькоxтипаnumber;- запись по индексу
scores[0] = "100"тоже не пройдёт: ошибкаTS2322, строку нельзя присвоить типуnumber; - чтение
scores[0]даётnumber, поэтому у элемента можно вызватьtoFixedи другие методы числа.
Две записи одного типа
Тип массива чисел можно записать двумя способами.
Запись number[]
Короткая запись: сначала тип элемента, потом []. Её чаще встречают в коде, и в курсе используется она.
Запись Array<number>
Длинная запись: слово Array и тип элемента в угловых скобках. Это тот же самый тип. Если написать const nums: Array<number> = [1, 2];, подсказка редактора всё равно покажет number[].
На собеседованиях иногда спрашивают, чем number[] отличается от Array<number>. Ничем: это две записи одного типа, и выбор между ними — вопрос стиля команды.
Массив из разных значений
Если в литерале лежат значения разных типов, TypeScript выводит массив объединения. Для const mixed = ["Аня", 30]; подсказка покажет (string | number)[]: каждый элемент — строка или число.
Скобки здесь меняют смысл:
(string | number)[]— массив, в котором любой элемент — строка или число;string | number[]— одна строка или массив чисел.
Табличка над таким рядом гласит «чемоданы или сумки». Достав вещь из шкафчика, служащий не знает, что у него в руках, и может делать с ней только то, что годится и для чемодана, и для сумки. С элементом mixed[0] так же: TypeScript разрешает только общее для string и number.
Здесь аналогия с камерой хранения заканчивается. Число шкафчиков в ряду задано заранее, а массив растёт: каждый push пристраивает в конец ряда новый шкафчик с той же табличкой.
Пустой массив
Из самого литерала [] вывести тип элементов не из чего: в нём нет ни одного элемента. Поэтому у пустого массива тип пишут сами: const names: string[] = [];.
Без аннотации TypeScript ведёт себя по-разному, смотря где лежит пустой массив:
- в переменной он следит за следующими строками: после
const names = [];иnames.push("Аня");уnamesтипstring[]; - но пока в массив ничего не положили, его нельзя ни передать в функцию, ни взять из него элемент: tsc сообщит о неявном типе
any[]; - в свойстве объекта TypeScript за следующими строками не следит и сразу выводит
never[].
Частая ошибка
Пустой массив положили в свойство объекта и не написали тип:
const cart = { items: [] };
cart.items.push("apple"); // ✗error TS2345: Argument of type '"apple"' is not assignable to parameter of type 'never'.У свойства items TypeScript вывел тип never[] — массив, в который нельзя положить ничего: тип never не принимает ни одного значения. Поэтому сообщение и говорит, что аргумент типа "apple" нельзя передать в параметр типа never.
Исправление — заранее назвать тип элементов:
type Cart = { items: string[] };
const cart: Cart = { items: [] };
cart.items.push("apple");
console.log(cart.items.length); // 1Главное
- Тип массива — тип элемента и скобки:
number[];Array<number>— другая запись того же типа. - TypeScript выводит тип из литерала:
[90, 75]— этоnumber[], иpush("100")в него — ошибкаTS2345. - Литерал из значений разных типов даёт массив объединения:
(string | number)[]. - Пустому массиву тип пишут сами, иначе в свойстве объекта он станет
never[].