Generics
Type parameters let one function or class work with many types while keeping them checked.
Generic functions
ts
function firstOr<T>(items: Array<T>, fallback: T): T {
if (items.length == 0) {
return fallback;
}
return items[0];
}
const n: int = firstOr([3, 4], 0);
const s: string = firstOr([], "none");
console.log(n, s);The checker infers T from the arguments, then checks the result. const s: string = firstOr([3, 4], 0); would be an error, because T is int there.
You can also pass the type yourself: firstOr<int>(scores, 0).
Generic classes
ts
class Stack<T> {
private items: Array<T> = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | null {
if (this.items.length == 0) {
return null;
}
return this.items.pop();
}
get size(): int {
return this.items.length;
}
}
const moves: Stack<string> = new Stack<string>();
moves.push("left");
moves.push("jump");
console.log(moves.pop(), moves.size);Write the type arguments in annotations
A type annotation must give every type argument. let m: Map = ... is an error. Write Map<string, int>, or Map<any, any> when you really mean any. A new expression can leave them out:
ts
const scores: Map<string, int> = new Map();
scores.set("ada", 10);
console.log(scores.get("ada")); // get answers int | undefined; use ! or ?? to get an intType arguments are checked
Array<int> and Array<string> are different types, and so are Promise<int> and Promise<string>. Arguments are covariant, as in TypeScript: an Array<int> can be used where an Array<float> is expected, but not the other way round.
ts
const ints: Array<int> = [1, 2];
const floats: Array<float> = ints;
console.log(floats.length);At run time
Generics are erased. Every Stack<T> shares the same compiled code, and there is no type information for T at run time. All the checking happens before the program runs.