Generic Functions

Here is the function from the previous exercise, written once for every type:

function first<T>(items: T[]): T | undefined {
    return items[0];
}

const names = ["Kira", "Bruno", "Lena"];
const prices = [30, 12, 55];

console.log(first(names));
console.log(first(prices));

This prints:

Kira
30

What's new is <T> after the function's name. It declares a type parameter called T. An ordinary parameter stands for a value that can be different at every call; a type parameter stands for a type that can be different at every call. The rest of the function uses T like any other type: items: T[] is an array of Ts, and the function returns a T (or undefined, more on that below).

At each call, TypeScript fills in T from the argument. In first(names), names is a string[], so T is string and the result is a string | undefined. In first(prices), T is number. A function with a type parameter is a generic function. Like the any version, it works with every array. Unlike it, TypeScript doesn't forget what went in:

function first<T>(items: T[]): T | undefined {
    return items[0];
}

const names = ["Kira", "Bruno", "Lena"];
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 reports:

main.ts(15,28): error TS2339: Property 'toUpperCase' does not exist on type 'number'.

leader.toUpperCase() is fine, because leader is a text. firstPrice is a number, and TypeScript tells you so before the program runs.

Why T | undefined? An empty array has no first element, so items[0] is undefined. TypeScript doesn't check array indexes, so we say it in the return type. Whoever calls first must then handle the missing value, as with find in lesson 5: with if (leader !== undefined), with leader?.name or with ??. Without a check, TypeScript reports 'leader' is possibly 'undefined'. (TS18048). Printing needs no check: console.log(first(names)) is fine, since console.log prints anything, undefined included.

The name T (for “type”) is just a habit. Any name works, for example function first<Element>(items: Element[]), but short names with a capital letter are usual. And like all types, type parameters disappear when the program runs. The JavaScript is just function first(items) { return items[0]; }.

Typical mistakes:

  • Not declaring the type parameter: function first(items: T[]) gives Cannot find name 'T'. (TS2304). T exists only after you write <T> after the function's name.
  • Using the result without checking for undefined (TS18048).

Instructions

Replace firstHero and firstPotion with one generic function, first<T>(items: T[]): T | undefined, and use it in both places. TypeScript will then tell you that leader is possibly undefined: write leader?.name in the template literal. The program prints the same lines as before.

Write a second generic function, last<T>(items: T[]): T | undefined, which returns the last element of the array: items[items.length - 1].

At the end of the program, use last to print the last potion and the name of the last hero, so that the program prints:

Leader: Kira
First potion: Healing Potion
Last potion: Elixir
Last hero: Bruno

Start programming for free

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

Or sign up with:

2/9