Generics in TypeScript
Generics, also known as type parameters, are a powerful feature in TypeScript that allow for the creation of reusable components.
As an example of a generic function, slicesIndex
takes an array of any type and an element of that type and returns the index of the first occurrence of v
in s
, or -1 if not present.
function slicesIndex<T>(s: T[], v: T): number {
for (let i = 0; i < s.length; i++) {
if (v === s[i]) {
return i;
}
}
return -1;
}
As an example of a generic type, List
is a singly-linked list with values of any type.
class List<T> {
private head: Element<T> | null = null;
private tail: Element<T> | null = null;
push(v: T): void {
if (this.tail === null) {
this.head = new Element(v);
this.tail = this.head;
} else {
this.tail.next = new Element(v);
this.tail = this.tail.next;
}
}
allElements(): T[] {
const elems: T[] = [];
let e = this.head;
while (e !== null) {
elems.push(e.val);
e = e.next;
}
return elems;
}
}
class Element<T> {
next: Element<T> | null = null;
constructor(public val: T) {}
}
Here’s how we can use these generic types and functions:
function main() {
const s = ["foo", "bar", "zoo"];
// When invoking generic functions, we can often rely
// on type inference. Note that we don't have to
// specify the type for T when calling slicesIndex -
// the compiler infers it automatically.
console.log("index of zoo:", slicesIndex(s, "zoo"));
// ... though we could also specify it explicitly.
slicesIndex<string>(s, "zoo");
const lst = new List<number>();
lst.push(10);
lst.push(13);
lst.push(23);
console.log("list:", lst.allElements());
}
main();
This code will output:
index of zoo: 2
list: [10, 13, 23]
In TypeScript, generics provide a way to create reusable components that work with a variety of types rather than a single one. This allows for writing flexible, reusable functions and classes without compromising type safety.