Тип Result
Некоторые функции могут потерпеть неудачу. Покупка в магазине не удаётся, если нужного предмета нет или если у героя не хватает денег. Функция могла бы возвращать undefined, но тогда вызывающий код не узнает, почему не получилось. Соедините то, что вы знаете, — размеченное объединение (урок «Моделирование данных») и параметр типа — и получите тип для результата всего, что может не удаться:
type Result<T> = { ok: true; value: T } | { ok: false; error: string };Result<T> — это либо успех со значением value типа T, либо неудача с сообщением об ошибке error. Тег — поле ok. Его типы — литеральные типы true и false: как и "potion", каждый допускает только это единственное значение. Одного типа хватает для всех функций, которые могут потерпеть неудачу: Result<number>, Result<Hero> и так далее.
type Result<T> = { ok: true; value: T } | { ok: false; error: string };
function shareGold(gold: number, heroes: number): Result<number> {
if (heroes === 0) {
return { ok: false, error: "нет героев" };
}
return { ok: true, value: gold / heroes };
}
const share = shareGold(120, 3);
if (share.ok) {
console.log(`Доля каждого героя: ${share.value}`);
} else {
console.log(`Не удалось поделить: ${share.error}`);
}Программа выводит:
Доля каждого героя: 40Внутри if (share.ok) TypeScript знает, что это успех, поэтому share.value — число. В ветке else это неудача, и у неё есть error. До проверки share мог быть любым из двух, поэтому использовать value нельзя. Добавьте в конец console.log(shareGold(120, 0).value); — и TypeScript сообщит:
main.ts(16,31): error TS2339: Property 'value' does not exist on type 'Result<number>'.
Property 'value' does not exist on type '{ ok: false; error: string; }'.Вторая строка объясняет: у неудачи нет value. В этом и смысл Result: как и с undefined от find, TypeScript заставляет каждого, кто вызывает функцию, обработать оба случая, поэтому неудачу нельзя забыть. И, в отличие от undefined, неудача ещё и говорит, что пошло не так. Многие программы на TypeScript используют этот приём.
Типичные ошибки:
- Смешать два случая:
return { ok: true, error: "…" };даётObject literal may only specify known properties, and 'error' does not exist in type '{ ok: true; value: number; }'.(TS2353). - Написать
ok: "true"(строку) вместоok: true. - Забыть аргумент типа: один
Resultдаёт TS2314,Generic type 'Result' requires 1 type argument(s).
Задание
- 1
Под магазином объявите тип
Result<T>в точности как выше:{ ok: true; value: T }или{ ok: false; error: string }. - 2
Напишите функцию
buyItem(shop: Item[], name: string, gold: number): Result<Item>. Она ищет предмет по названию черезfind. Если в магазине такого предмета нет, она возвращает неудачу с ошибкой`${name} — нет в магазине`. Если предмет стоит дороже, чемgold, — с ошибкой`${name} — не хватает золота`. Иначе она возвращает успех, а значением будет сам предмет. - 3
У Киры 50 золотых, и она пытается купить три вещи. Добавьте в конец программы такой цикл:
for (const name of ["Лечебное зелье", "Серебряный меч", "Драконье яйцо"]) { const result = buyItem(shop, name, 50); if (result.ok) { console.log(`Куплено: ${result.value.name}`); } else { console.log(`Не удалось: ${result.error}`); } }Программа выведет:
Куплено: Лечебное зелье Не удалось: Серебряный меч — не хватает золота Не удалось: Драконье яйцо — нет в магазине