Ограничения
Напишем обобщённую функцию, которая находит самого сильного среди всего, у чего есть power: героев, монстров, заклинаний.
function strongest<T>(things: T[]): T {
let best = things[0];
for (const thing of things) {
if (thing.power > best.power) {
best = thing;
}
}
return best;
}TypeScript сообщает:
main.ts(4,19): error TS2339: Property 'power' does not exist on type 'T'.
main.ts(4,32): error TS2339: Property 'power' does not exist on type 'T'.TypeScript прав: T может быть любым типом, поэтому strongest можно вызвать с массивом чисел или строк, а у них нет power. Нужно сказать, какие типы разрешены. Это ограничение:
type Hero = { name: string; power: number };
type Monster = { kind: string; power: number; loot: string };
function strongest<T extends { power: number }>(things: T[]): T {
let best = things[0];
for (const thing of things) {
if (thing.power > best.power) {
best = thing;
}
}
return best;
}
const heroes: Hero[] = [
{ name: "Кира", power: 7 },
{ name: "Бруно", power: 9 },
];
const monsters: Monster[] = [
{ kind: "гоблин", power: 3, loot: "Ржавый кинжал" },
{ kind: "тролль", power: 8, loot: "Большая дубина" },
];
console.log(strongest(heroes).name);
console.log(strongest(monsters).loot);Программа выводит:
Бруно
Большая дубинаT extends { power: number } означает: T может быть любым типом, у которого есть power — число (и, кроме него, что угодно ещё). Это то же слово, что и в interface Boss extends Monster из урока «Типы объектов»: «подходит под эту форму и может иметь что-то сверх неё». Внутри функции thing.power теперь разрешён. А результат сохраняет полный тип: для героев strongest возвращает Hero, поэтому работает .name; для монстров — Monster со своим loot.
Вызовы с массивами, которые не подходят под ограничение, — ошибки. Добавьте в конец программы две строки:
const names = ["Кира", "Бруно"];
console.log(strongest(names));TypeScript сообщает:
main.ts(26,23): error TS2345: Argument of type 'string[]' is not assignable to parameter of type '{ power: number; }[]'.
Type 'string' is not assignable to type '{ power: number; }'.Почему не обойтись без обобщений? Функция strongest(things: { power: number }[]): { power: number } принимает те же массивы, но возвращает всего лишь { power: number }. TypeScript забывает, что это был герой, и strongest(heroes).name даёт Property 'name' does not exist on type '{ power: number; }'. (TS2339). С ограничением вы получаете и то и другое: ограничение говорит, что нужно функции, а параметр типа сохраняет то, что есть у вызывающего кода.
Ограничением может быть любой тип, в том числе именованный: если есть type Strong = { power: number };, пишите <T extends Strong>.
Задание
- 1
Напишите обобщённую функцию
findByName<T extends { name: string }>(things: T[], name: string): T | undefined. Она возвращает первый элемент, у которого свойствоnameравно параметруname, илиundefined, если такого нет; используйтеfind. - 2
С помощью
findByNameвыведитеheroClassБруно,priceСеребряного меча и то, что функция находит в магазине по названию"Драконье яйцо". (Результат может бытьundefined, поэтому используйте?..) Программа выведет:воин 120 undefined