Generics in TypeScript: a function that keeps the type it was given

A generic leaves the type as a parameter so a function returns what it received: inference, the extends keyof constraint, and the built-in generics.
3 min read
Believemy logo

Writing a function that returns the first element of an array takes one line. Making it useful for numbers and for invoices alike, without losing the type along the way, takes something more.

That is what a generic is for: leaving the type as a parameter, and recovering it at the call site.


Definition

A generic is a type parameter. It is declared between angle brackets, next to the function name, and behaves like a variable whose value is a type, settled at the moment of the call.

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

const n = first([1, 2, 3]);     // number | undefined
const s = first(["a", "b"]);    // string | undefined

Nobody wrote first<number>: the compiler works T out from the argument it received. Without a generic the return would have been annotated any, and the whole verification chain would have collapsed after that call.


Constraining the type parameter

A free T allows no operation at all, since it could be anything. The extends keyword restricts the set of accepted types, and unlocks whatever becomes safe as a result.

JAVASCRIPT
function byKey<T, K extends keyof T>(object: T, key: K): T[K] {
  return object[key];
}

const invoice = { id: 42, amount: 19 };
const m = byKey(invoice, "amount");   // number
byKey(invoice, "client");
// Argument of type '"client"' is not assignable
// to parameter of type '"id" | "amount"'.

Two type parameters are enough here to express a rule the language alone cannot state: the requested key exists on the object, and the return has the type of that precise key.


The generics already everywhere

NotationWhat the parameter stands for
Array<T>The type of the elements of an Array
Promise<T>The value a fulfilled Promise will hand back
Map<K, V>The key type then the value type of a Map
Record<K, V>An object whose keys and values are both announced
Partial<T>The same type, with every property turned optional
Good to know

These five notations show up within the first hour of TypeScript. Understanding generics mostly means no longer reading them as magic syntax.


Frequently asked questions

Question

Why not simply write any?

Because any switches checking off instead of carrying it forward. A function taking any and returning any lets every downstream typo through. A generic keeps the link between what goes in and what comes out, which is precisely the useful piece of information.


Question

How many type parameters are reasonable?

One in the vast majority of cases, two when a correspondence is involved, such as a key and its value. Past three, the signature becomes harder to read than the problem it solves, and a named interface almost always does better.


Question

Must the type be spelled out at every call?

Rarely. Inference covers most situations, and a hand-written type between angle brackets often signals that the signature lacks a constraint. The legitimate case is a call with no argument carrying information, such as a request where only the return is typed.

Related terms

Discover our javaScript glossary

Every word of JavaScript explained simply: keywords, built-in objects, methods, errors and concepts. Clear definitions and examples that actually run, to learn and to troubleshoot.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.