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
30What'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[])givesCannot find name 'T'.(TS2304).Texists 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: BrunoStart programming for free
2/9