$ node first.jsAtsctype checker!✓1function first<T>(items: T[]): T {2 return items[0];3}4const n = first([10, 20]);5const s = first(["a", "b"]);6console.log(s.toFixed(1));console.log(s.toUpperCase());Tns?numberstringnumberstringnumber[]string[]numberstringTSJS
Back row: first.ts (a generic function) and tsc, the type checkerWhat tsc knows: T for the current call, and the types of n and s. Behind the card: the laptop that runs the JavaScriptTiles: the argument types going into tsc and the types it works out coming backfirst.ts holds a generic function first<T> and three lines that use it; tsc, the type checker, is at the back right. In front: the code card and what tsc knows (T, n and s); behind the card, a laptop that runs the JavaScript. Each call fills in T from its argument: number[] gives T = number, so n is a number; string[] gives T = string, so s is a string. That is how tsc catches s.toFixed on line 6. After the fix the types are erased and the program prints A.

The idea

  1. first returns the first item of a list. It should work for a list of numbers, a list of text, a list of anything. Without generics you write one copy per type, or use any and lose type checking.

Each call fills in T

Bugs caught

Run it

What happens

;; step 1/9 · why
first.ts  (TypeScript)
> 1  function first<T>(items: T[]): T {
> 2      return items[0];
  3  }
  4  const n = first([10, 20]);
  5  const s = first(["a", "b"]);
  6  console.log(s.toFixed(1));

without generics:
  firstNumber(items: number[]): number
  firstString(items: string[]): string   a copy per type
  firstAny(items: any[]): any            checking switched off

You can also write T yourself: first<number>([10, 20]). Usually you leave it out and tsc infers it from the arguments, as here. The first.js shown is what tsc 5.9 writes with --target es2022. With no settings tsc 5.9 targets old JavaScript (ES5) and writes var instead of const; the types are removed either way.