A Result Type
Some functions can fail. Buying in a shop fails when the shop doesn't have the item, or when the hero can't afford it. A function could return undefined when it fails, but then the caller doesn't know why. Combine what you know, a discriminated union (lesson 6) and a type parameter, and you get a type for the result of anything that can fail:
type Result<T> = { ok: true; value: T } | { ok: false; error: string };A Result<T> is either a success, with a value of type T, or a failure, with an error message. The tag is ok. Its types are the literal types true and false: like "potion", each allows only that one value. One type is enough for all functions that can fail: Result<number>, Result<Hero>, and so on.
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: "nobody to share with" };
}
return { ok: true, value: gold / heroes };
}
const share = shareGold(120, 3);
if (share.ok) {
console.log(`Each hero gets ${share.value} gold`);
} else {
console.log(`Can't share: ${share.error}`);
}This prints:
Each hero gets 40 goldInside if (share.ok), TypeScript knows it's a success, so share.value is a number. In the else branch, it's a failure, which has an error. Before the check, share could be either, so using value is an error. Add console.log(shareGold(120, 0).value); at the end, and TypeScript reports:
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; }'.The second line explains it: a failure has no value. That's the point of Result: as with the undefined from find, TypeScript makes every caller deal with both cases, so a failure can't be forgotten. And unlike undefined, a failure also says what went wrong. Many TypeScript programs use this pattern.
Typical mistakes:
- Mixing the two cases:
return { ok: true, error: "…" };givesObject literal may only specify known properties, and 'error' does not exist in type '{ ok: true; value: number; }'.(TS2353). - Writing
ok: "true"(a text) instead ofok: true. - Leaving out the type argument:
Resultalone gives TS2314,Generic type 'Result' requires 1 type argument(s).
Instructions
Below the shop, declare the type Result<T> exactly as above: { ok: true; value: T } or { ok: false; error: string }.
Write a function buyItem(shop: Item[], name: string, gold: number): Result<Item>. It finds the item by its name with find. If the shop doesn't have it, it fails with the error `no ${name} in the shop`. If the item costs more than gold, it fails with `not enough gold for ${name}`. Otherwise it succeeds, with the item as the value.
Kira has 50 gold and tries to buy three things. Add this loop at the end of the program:
for (const name of ["Healing Potion", "Silver Sword", "Dragon Egg"]) {
const result = buyItem(shop, name, 50);
if (result.ok) {
console.log(`Bought: ${result.value.name}`);
} else {
console.log(`Failed: ${result.error}`);
}
}The program prints:
Bought: Healing Potion
Failed: not enough gold for Silver Sword
Failed: no Dragon Egg in the shopStart programming for free
6/9