Функции в map и filter
У массивов есть методы, которые принимают функцию как аргумент. Два из них используются постоянно, и они для вас новые: map и filter. Оба — обычный JavaScript, но TypeScript добавляет к ним кое-что полезное.
map создаёт новый массив. Он вызывает вашу функцию для каждого элемента и кладёт то, что она вернула, в новый массив в том же порядке:
const chests = [10, 25, 40];
const doubled = chests.map(gold => gold * 2);
console.log(doubled);
console.log(chests);Это выведет:
[20, 50, 80]
[10, 25, 40]map вызвал gold => gold * 2 три раза, с 10, 25 и 40, и собрал результаты. Исходный массив chests не изменился. Это то же, что делает такой цикл, только в одну строку:
const doubled: number[] = [];
for (const gold of chests) {
doubled.push(gold * 2);
}Функция может возвращать значение другого типа. Тогда элементы нового массива будут этого типа:
const names = ["Кира", "Бруно", "Лена"];
console.log(names.map(name => name.toUpperCase()));
console.log(names.map(name => name.length));Это выведет:
["КИРА", "БРУНО", "ЛЕНА"]
[4, 5, 4]Обратите внимание, что у gold и name нет типа, и TypeScript не возражает. В этом и польза: TypeScript знает, что chests — это number[], поэтому функция, переданная в map, получает числа, и gold — это number. Параметр получает тип от места, где записана функция (программисты называют это контекстной типизацией). TypeScript знает и то, что возвращает map: по gold * 2 — массив чисел, number[]. А поскольку он знает, что name — строка, он проверяет, что вы с ней делаете:
const names = ["Кира", "Бруно", "Лена"];
const doubled = names.map(name => name * 2);main.ts(2,35): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.Это работает, только когда функция записана прямо там, где используется, в виде аргумента. Стрелочной функции, сохранённой отдельно в переменной, типы всё равно нужны, потому что TypeScript не знает, где её будут использовать:
const double = gold => gold * 2;main.ts(1,16): error TS7006: Parameter 'gold' implicitly has an 'any' type.filter тоже создаёт новый массив, но оставляет в нём только некоторые элементы: те, для которых ваша функция вернула true:
const monsters = [
{ name: "гоблин", power: 4 },
{ name: "тролль", power: 15 },
{ name: "волк", power: 8 },
{ name: "дракон", power: 40 },
];
const strong = monsters.filter(monster => monster.power > 10);
console.log(strong);
console.log(strong.map(monster => monster.name));Это выведет:
[{ name: "тролль", power: 15 }, { name: "дракон", power: 40 }]
["тролль", "дракон"]Здесь monster получает тип элементов массива, { name: string; power: number; }, поэтому TypeScript ловит опечатку:
const monsters = [
{ name: "гоблин", power: 4 },
{ name: "тролль", power: 15 },
{ name: "волк", power: 8 },
{ name: "дракон", power: 40 },
];
const strong = monsters.filter(monster => monster.pwer > 10);main.ts(7,51): error TS2551: Property 'pwer' does not exist on type '{ name: string; power: number; }'. Did you mean 'power'?У результата filter тот же тип, что у исходного массива, так что с ним можно продолжать работу. В последней строке примера у результата вызывается map, чтобы получить только названия.
Типичные ошибки:
- Фигурные скобки без
return:chests.map(gold => { gold * 2 })даёт[undefined, undefined, undefined], а та же ошибка вfilterдаёт[]. TypeScript здесь о ней не сообщает, поэтому, если используете фигурные скобки, пишитеreturn. - Ожидание, что
mapилиfilterизменят массив. Они его не меняют: они возвращают новый массив. Сохраните его в переменную или сразу используйте.
Задание
- 1
Счастливый день: золото в каждом сундуке удваивается. Вызовите
mapуchests, чтобы создать массивdoubledGoldс удвоенным золотом каждого сундука. - 2
Выведите
doubledGold, а затемchests, чтобы увидеть, чтоmapне изменил исходный массив. Программа выведет:[24, 60, 14, 90] [12, 30, 7, 45] - 3
Кира сражается только с сильными монстрами, у которых сила больше 10. Вызовите
filterуmonsters, чтобы создать массивstrongMonstersс ними. - 4
Выведите
strongMonsters, а затем только имена сильных монстров: вызовитеmapуstrongMonsters. В конце программа выведет:[{ name: "тролль", power: 25 }, { name: "скелет", power: 12 }] ["тролль", "скелет"]