Массивы только для чтения
Функция получает не собственную копию массива, а тот же массив, что и у вызывающего кода. Поэтому она может изменить чужой массив. Эта функция должна посчитать зелья в сумке, и она считает, но посмотрите на сумку после этого:
const bag = ["Зелье", "Верёвка", "Зелье"];
function countPotions(items: string[]): number {
let count = 0;
while (items.length > 0) {
if (items.pop() === "Зелье") {
count++;
}
}
return count;
}
console.log(countPotions(bag));
console.log(bag);Программа выводит:
2
[]pop() достаёт последний элемент из массива, поэтому после подсчёта сумка пуста. В большой программе такой баг найти трудно: сумка опустела в функции, которая будто бы только считала. TypeScript может этого не допустить. Напишите readonly перед типом массива:
function countPotions(items: readonly string[]): number {main.ts(6,19): error TS2339: Property 'pop' does not exist on type 'readonly string[]'.readonly string[] — это массив, который можно читать, но нельзя менять. Чтение элемента, length, for...of, includes, find, map и filter работают (map и filter создают новый массив). А вот push, pop и присваивания элементам нет: items[0] = "Меч" даёт Index signature in type 'readonly string[]' only permits reading. В параметре readonly — это обещание, которое вызывающий код видит уже в первой строке функции: эта функция только читает массив.
Тот же тип можно записать как ReadonlyArray<string>, так же как string[] можно записать как Array<string>. В сообщениях об ошибках TypeScript пишет readonly string[].
Обычный массив можно передать в параметр с readonly: функция его только читает. Наоборот не получится. Массив только для чтения, например список, созданный с помощью as const в уроке про моделирование данных, нельзя передать в параметр string[], потому что та функция могла бы его изменить:
const classes = ["warrior", "mage", "ranger"] as const;
function printAll(names: string[]): void {
for (const name of names) {
console.log(name);
}
}
printAll(classes);main.ts(9,10): error TS4104: The type 'readonly ["warrior", "mage", "ranger"]' is 'readonly' and cannot be assigned to the mutable type 'string[]'.Поэтому функция, которая только читает массив, должна принимать readonly-массив: тогда подойдут оба вида. А если ей нужно вернуть массив с чем-то добавленным, она создаёт новый с помощью spread: [...items, "Старая карта"].
Для объектов есть вспомогательный тип: Readonly<Hero> — это Hero, у которого каждое свойство readonly. Это всё равно что написать readonly перед каждым свойством, как в уроке про типы объектов, но только там, где вы его используете: hero: Readonly<Hero> в параметре означает, что функция не меняет героя, а hero.health = 0 внутри неё даёт Cannot assign to 'health' because it is a read-only property. Как и любой тип, readonly проверяет только TypeScript. JavaScript, который выполняется, о нём не знает.
Типичные ошибки:
readonlyпишется только перед типами с[].readonly Array<string>даёт'readonly' type modifier is only permitted on array and tuple literal types.Напишитеreadonly string[]илиReadonlyArray<string>.- Возврат самого параметра из функции, которая возвращает
string[]:The type 'readonly string[]' is 'readonly' and cannot be assigned to the mutable type 'string[]'.Верните новый массив.
Задание
- 1
Функция
withItemдолжна вернуть новый список, но меняет тот, который получила. Измените тип её параметраitemsнаreadonly string[]и запустите программу: TypeScript укажет на строку сpush. Замените тело функции наreturn [...items, item];. - 2
Под функциями выведите
withItem(inventory, "Старая карта"), а затемinventory. Инвентарь не изменился:["Лечебное зелье", "Верёвка", "Факел", "Старая карта"] ["Лечебное зелье", "Верёвка", "Факел"] - 3
Функция
healthTextтоже не должна менять героя. Измените тип её параметра наReadonly<Hero>и запустите программу. TypeScript найдёт в функции строку, которая всё-таки меняет героя. Исправьте ошибку: один знак=присваивает значение, а вам нужно сравнить. - 4
Под остальными строками выведите
healthText(kira):Кира: 34/50