Generic Types

You can declare your own generic types too. A shop sells boxes. Every box has a label, but what's inside differs: coins, arrows, a sword. Instead of a type for each kind of box, declare one type with a type parameter:

type Box<T> = { label: string; content: T };

The type parameter goes in angle brackets after the type's name, and inside the type, T is used like any other type. Box<number> is then { label: string; content: number }, and Box<string[]> is a box with an array of texts. TypeScript checks the content against the type argument:

type Box<T> = { label: string; content: T };

const coins: Box<number> = { label: "Coins", content: 50 };
const arrows: Box<string[]> = { label: "Arrows", content: ["Fire Arrow", "Ice Arrow"] };
const sword: Box<number> = { label: "Sword", content: "Silver Sword" };

TypeScript reports:

main.ts(5,46): error TS2322: Type 'string' is not assignable to type 'number'.

The box sword says it holds a number. For a variable, you always write the type argument; without it, const sword: Box = … gives Generic type 'Box' requires 1 type argument(s). (TS2314), as with Array.

Interfaces can have type parameters as well, and a type can have several of them:

interface Shelf<T> {
    name: string;
    items: T[];
}

type Pair<A, B> = [A, B];

const potionShelf: Shelf<string> = { name: "Potions", items: ["Healing Potion", "Elixir"] };
const priceTag: Pair<string, number> = ["Silver Sword", 120];

Generic types in generic functions. A function that accepts any box is generic too:

type Box<T> = { label: string; content: T };

function unpack<T>(box: Box<T>): T {
    console.log(`Unpacking: ${box.label}`);
    return box.content;
}

const coins: Box<number> = { label: "Coins", content: 50 };
const gold = unpack(coins) + 20;
console.log(gold);

This prints:

Unpacking: Coins
70

In unpack(coins), TypeScript sees that coins is a Box<number>, so T is number and unpack returns a number: adding 20 to it is fine. For a Box<string[]>, it would return a string[].

Instructions

Declare a generic type Chest<T> for a chest with a location (a text) and loot of type T. Then create two chests: goldChest of type Chest<number> in "Goblin Cave" with the loot 120, and potionChest of type Chest<string[]> in "Wizard Tower" with the loot ["Healing Potion", "Elixir"].

Write a generic function openChest<T>(chest: Chest<T>): T. It prints Opening the chest in followed by the chest's location, for example Opening the chest in Goblin Cave, and returns the chest's loot.

Kira opens both chests. Add these lines at the end of the program:

gold = gold + openChest(goldChest);
for (const potion of openChest(potionChest)) {
    inventory.push(potion);
}
console.log(`Gold: ${gold}`);
console.log(inventory);

The program prints:

Opening the chest in Goblin Cave
Opening the chest in Wizard Tower
Gold: 170
["Bow", "Healing Potion", "Elixir"]

TypeScript knows that the first call returns a number, which can be added to gold, and the second one a string[], whose elements fit into inventory.

Start programming for free

By signing up, you agree to the Terms of Service and Privacy Policy.

Or sign up with:

5/9