Type Arguments

When you call first(names), you don't say what T is: TypeScript works it out from the argument. That's type inference again, this time for a type parameter. You can also write the type yourself, in angle brackets between the function's name and the parentheses:

const leader = first<string>(names);

The type in the angle brackets is a type argument. It goes into the type parameter T, just as the argument names goes into the parameter items. Here it changes nothing, since TypeScript would pick string anyway. But TypeScript now checks the arguments against the type you wrote:

function first<T>(items: T[]): T | undefined {
    return items[0];
}
const potions = ["Healing Potion", "Mana Potion"];
console.log(first<string>(potions));
console.log(first<number>(potions));

TypeScript reports:

main.ts(6,27): error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'number[]'.
  Type 'string' is not assignable to type 'number'.

With T set to number, the parameter items: T[] becomes a number[], and an array of texts doesn't fit. The indented second line says why: a text isn't a number. Longer messages often have such lines; read them from the top, each one goes into more detail.

A type parameter can appear in several places, and they must all agree:

function pairUp<T>(first: T, second: T): T[] {
    return [first, second];
}

console.log(pairUp("Kira", "Bruno"));
console.log(pairUp("Kira", 5));

TypeScript reports:

main.ts(6,28): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

From the first argument, "Kira", TypeScript decided that T is string, so the second argument must be a text too.

A function can also have several type parameters, separated by commas. This one makes a tuple (lesson 2) from any two values:

function pair<A, B>(first: A, second: B): [A, B] {
    return [first, second];
}

const potionCount = pair("Healing Potion", 3);
console.log(potionCount);

This prints:

["Healing Potion", 3]

Here A is string and B is number, so potionCount is a [string, number].

When to write type arguments. Usually, you don't: let TypeScript infer them. You need them when TypeScript would infer a type that is too narrow. This function fills three backpack slots with a value, and Kira starts with empty slots, null:

function fill<T>(value: T): T[] {
    return [value, value, value];
}

const slots = fill(null);
slots[1] = "Shield";
console.log(slots);

TypeScript reports:

main.ts(6,1): error TS2322: Type '"Shield"' is not assignable to type 'null'.

From null, TypeScript inferred that T is null, so slots is an array that can only ever hold null. Write the type you mean as the type argument, const slots = fill<string | null>(null);, and the slots can hold texts or null. The program then prints:

[null, "Shield", null]

Instructions

Write a generic function repeat<T>(value: T, times: number): T[] that returns a new array in which value appears times times. Start with an empty array, const result: T[] = [];, and push the value into it in a for loop.

Below the function, create const arrows = repeat("Arrow", 3); and print arrows:

["Arrow", "Arrow", "Arrow"]

Kira's backpack has four slots, empty at first. Create them with repeat in a variable slots: null, four times, with the type argument string | null, so that the slots can hold texts later. Then put "Sword" into the first slot (index 0) and print slots. The program prints:

["Arrow", "Arrow", "Arrow"]
["Sword", null, null, null]

Start programming for free

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

Or sign up with:

3/9