Обобщённые функции
Вот функция из прошлого упражнения, написанная один раз для всех типов:
function first<T>(items: T[]): T | undefined {
return items[0];
}
const names = ["Кира", "Бруно", "Лена"];
const prices = [30, 12, 55];
console.log(first(names));
console.log(first(prices));Программа выводит:
Кира
30Новое здесь — <T> после имени функции. Так объявляется параметр типа с именем T. Обычный параметр обозначает значение, которое при каждом вызове может быть разным; параметр типа обозначает тип, который при каждом вызове может быть разным. Остальная часть функции использует T как любой другой тип: items: T[] — массив из элементов типа T, а функция возвращает T (или undefined, об этом ниже).
При каждом вызове TypeScript подставляет T по аргументу. В first(names) names — это string[], поэтому T — это string, а результат — string | undefined. В first(prices) T — это number. Функция с параметром типа называется обобщённой функцией. Как и версия с any, она работает с любым массивом. Но, в отличие от неё, TypeScript не забывает, что в неё попало:
function first<T>(items: T[]): T | undefined {
return items[0];
}
const names = ["Кира", "Бруно", "Лена"];
const prices = [30, 12, 55];
const leader = first(names);
if (leader !== undefined) {
console.log(leader.toUpperCase());
}
const firstPrice = first(prices);
if (firstPrice !== undefined) {
console.log(firstPrice.toUpperCase());
}TypeScript сообщает:
main.ts(15,28): error TS2339: Property 'toUpperCase' does not exist on type 'number'.leader.toUpperCase() допустим, потому что leader — строка. А firstPrice — число, и TypeScript говорит об этом ещё до запуска программы.
Почему T | undefined? В пустом массиве нет первого элемента, поэтому items[0] — это undefined. TypeScript не проверяет индексы массивов, поэтому мы сообщаем об этом в возвращаемом типе. Тот, кто вызывает first, должен обработать отсутствующее значение, как с find в уроке «Объединения и сужение типов»: через if (leader !== undefined), через leader?.name или через ??. Без проверки TypeScript сообщит 'leader' is possibly 'undefined'. (TS18048). Для вывода проверка не нужна: console.log(first(names)) работает, ведь console.log выводит что угодно, в том числе undefined.
Имя T (от «type») — просто привычка. Подойдёт любое имя, например function first<Element>(items: Element[]), но обычно берут короткие имена с заглавной буквы. И, как и все типы, параметры типа исчезают, когда программа запускается. В JavaScript остаётся просто function first(items) { return items[0]; }.
Типичные ошибки:
- Не объявить параметр типа:
function first(items: T[])даётCannot find name 'T'.(TS2304).Tсуществует только после того, как вы напишете<T>после имени функции. - Использовать результат без проверки на
undefined(TS18048).
Задание
- 1
Замените
firstHeroиfirstPotionодной обобщённой функциейfirst<T>(items: T[]): T | undefinedи используйте её в обоих местах. Тогда TypeScript сообщит, чтоleaderможет бытьundefined: напишите в шаблонной строкеleader?.name. Программа выведет те же строки, что и раньше. - 2
Напишите вторую обобщённую функцию,
last<T>(items: T[]): T | undefined, которая возвращает последний элемент массива:items[items.length - 1]. - 3
В конце программы выведите с помощью
lastпоследнее зелье и имя последнего героя, чтобы программа выводила:Лидер: Кира Первое зелье: Лечебное зелье Последнее зелье: Эликсир Последний герой: Бруно