Поиск вещей
До сих пор основная программа выбирала предметы по их позиции в массиве. Это ненадёжно: стоит добавить предмет в начало инвентаря, и kira.inventory[0] вдруг окажется другим. Лучше искать предмет по имени. А функция, которая ищет по имени, пригодится не только для предметов: у героя в отряде, монстра в пещере и заклинания в книге тоже есть имена. Поэтому это будет обобщённая функция (generic), как в уроке «Обобщения».
Параметр типа T — это тип вещей в массиве. От них функции нужно только одно, name, и именно это говорит ограничение: T extends { name: string }. Без него TypeScript не знает, есть ли у T имя вообще: main.ts(63,39): error TS2339: Property 'name' does not exist on type 'T'.
Метод массива find из урока «Объединения и сужение типов» возвращает первый элемент, для которого функция вернула true, или undefined, если такого нет. Поэтому функция возвращает T | undefined, и тот, кто её вызывает, должен обработать оба случая. Другой пример: функция, которая ищет что-нибудь заданного уровня:
function findByLevel<T extends { level: number }>(things: T[], level: number): T | undefined {
return things.find(thing => thing.level === level);
}
const party = [
{ name: "Бруно", level: 5 },
{ name: "Лена", level: 2 },
];
const quests = [
{ title: "Крысы в погребе", level: 1 },
{ title: "Пещера тролля", level: 5 },
];
const hero = findByLevel(party, 2);
console.log(hero?.name);
const quest = findByLevel(quests, 3);
if (quest === undefined) {
console.log("Квеста для уровня 3 нет");
} else {
console.log(quest.title);
}Это выведет:
Лена
Квеста для уровня 3 нетОдна функция работает и для героев, и для квестов, а результат сохраняет свой полный тип: quest.title допустимо, потому что там T — тип квеста. Но TypeScript разрешает использовать quest.title только после проверки на undefined.
Основная программа попробует использовать три предмета по именам: зелье, меч и драконье яйцо, которого у Киры нет. Цикл for...of может пройти по массиву, записанному прямо в нём: for (const name of ["Целебное зелье", "Серебряный меч", "Драконье яйцо"]). Если передать результат findByName прямо в useItem, без проверки, TypeScript сообщит об этом:
main.ts(90,31): error TS2345: Argument of type 'Item | undefined' is not assignable to parameter of type 'Item'.
Type 'undefined' is not assignable to type 'Item'.Задание
- 1
Под
useItemнапишите обобщённую функциюfindByName<T extends { name: string }>(things: T[], name: string): T | undefined, которая с помощьюfindвозвращает первую вещь с таким именем. - 2
В основной программе замените две строки с
useItemцикломfor...ofпо именам"Целебное зелье","Серебряный меч"и"Драконье яйцо". Для каждого имени найдите предмет в инвентаре Киры с помощьюfindByName. Если его там нет, выведитеКира не нашла предмет «Драконье яйцо»(имя героя и имя, которое вы искали); иначе выведите то, что возвращаетuseItem. Программа заканчивается так:Кира пьёт «Целебное зелье»: здоровье 50/50 Кира берёт в руку «Серебряный меч» Кира не нашла предмет «Драконье яйцо»