TypeScript Tutorial
TypeScript Generics
A generic writes one function for many types. The compiler fills in number, string, or your class.
One recipe, many types
You already saw function overloading: two functions with the same name and different signatures. The bodies were often the same idea copied twice. A generic is that idea written once. You name a placeholder type, usually T. At each call, the compiler substitutes the real type and checks the body.
That substitution happens at compile time. There is no extra decision while the program runs. Generics are the TypeScript analog of C++ templates. C++ writes template <typename T>. TypeScript writesfunction id<T>(x: T): T.
function id<T>(x: T): T
The identity function returns what you pass in. Prefix the name with <T>. UseT as the parameter type and the return type. The same source covers number andstring.
Example
function id<T>(x: T): T {
return x;
}
console.log(id<number>(7));
console.log(id("ok"));Output is 7 then ok. The first call writes the type argumentid<number>(7). The second lets the compiler infer T as string from the argument. You did not write two functions.
Run this in /typescript/try with Try it in TypeScript. Swap the values. Do not paste it into the C++ editor at /cpp/try.
The compiler fills in T
Both arguments must agree on T when a function takes two values of one parameter type. A helper that returns the larger of two numbers or two strings needs a constraint so > is legal.
Example
function bigger<T extends number | string>(a: T, b: T): T {
return a > b ? a : b;
}
console.log(bigger(3, 9));
console.log(bigger("ada", "kai"));Output is 9 then kai. extends number | string limits T to types that compare with > in this tutorial. bigger(3, "9") does not compile: oneT cannot be both.
| Call | T becomes |
|---|---|
id(7) | number |
id("ok") | string |
bigger(3, 9) | number |
bigger("ada", "kai") | string |
A tiny generic class: Box
A generic class is the same placeholder idea on a type. Box<T> holds one value of whateverT you name. Box<number> and Box<string> are two different types. You cannot assign one to the other without converting the contents yourself.
Example
class Box<T> {
constructor(public value: T) {}
}
const count = new Box<number>(4);
const title = new Box<string>("draft");
console.log(count.value);
console.log(title.value);Output is 4 then draft. Array<number>,Map<string, number>, and Set<string> in later chapters are generic types of this kind, with more members.
Generics versus overloading
Overload when the bodies differ: one branch for a string, another that loops through a list. Use a generic when the body is the same and only the type changes. You can mix them later, but you do not need that mix to start.
Write the generic in the same file as the call for now, above the first use. Splitting across modules is the modules chapter: export function id<T>(x: T): T in one local file, import in another. These examples stay in one file so /typescript/try can compile them as-is.
The type system has a library too
Built-in utility types are generics that reshape other types: Partial<User>,Pick<User, "name">. They are the next chapter — TypeScript’s analog of a small STL, but for types, not containers.