Lesson 13 · Module 2.1 — Generics
<T> placeholder and where it livesA generic is not a type. It is a type parameter — a placeholder
that gets filled with a real type each time the thing is used. A natural question is “what are all
the ways to declare one?” The honest answer: there are only a handful of positions, and
once you can spot the <…>, you can read every one of them.
Reading <T> is exactly like reading a function parameter — but for types.
function f(x) says “x is filled in when you call f.”
function f<T>(x: T) says “T is filled in when you call f too.”
Same idea, one level up.
This trips people up, and for good reason: the two terms get used interchangeably. The official
word is type parameter (the T convention literally stands for it).
Either way it means the same: a placeholder for a type that is unknown when you write the
code and chosen when you use it.
A type parameter is to a type what a function parameter is to a value: a slot filled in per use, not a fixed answer baked in at the definition.
The <T> always sits just before the parameter list or the name being
defined. Here are the four places you will actually meet:
// 1) generic function declaration — <T> before the ( )
function identity<T>(arg: T): T { return arg; }
// 2) generic arrow function — <T,> before the ( )
const identity2 = <T,>(arg: T): T => arg;
// 3) generic interface — <T> after the name
interface Container<T> { value: T; process(input: T): T; }
// 4) generic class — <T> after the name
class Box<T> { constructor(public value: T) {} }
In every case, read <T> as “introduce a placeholder named T; the
types below may refer to it.” The <T,> trailing comma on the arrow is just a
disambiguation trick (so the compiler doesn’t read <T> as JSX) — same meaning.
Both of these live after a type keyword, but the <…> sits in a
different spot, and that changes when T is chosen:
// A) generic function TYPE — <T> is inside, after the =. T is resolved PER CALL.
type GenericFn = <T>(x: T) => T;
// B) parameterized ALIAS — <T> is on the NAME. T is fixed WHEN YOU USE the alias.
type Predicate<T> = (value: T) => boolean;
With GenericFn, the variable holds one function that re-infers T on each
invocation. With Predicate<T>, you pin T at the moment you name the type:
const fn: GenericFn = x => x;
fn(123); // T = number for THIS call
fn("abc"); // T = string for THIS call
const isPositive: Predicate<number> = x => x > 0; // T fixed as number, here and forever
So: <T> on the name means the caller picks T when they
mention the type. <T> on the function means each call picks its own.
Recall, don’t re-read. (Answers reveal on click.)
function identity<T>(arg: T): T, the <T> is best read as…type Predicate<T> = (value: T) => boolean, the type parameter T is…T resolves per call?The official page is
TypeScript Handbook — Generics;
the “Generic Types” section there names the same function-type-vs-alias distinction.
Best way to feel it: paste GenericFn and Predicate<T> into the
TS Playground and watch where you’re allowed to put the <number>.
Got a <T> somewhere and can’t tell if the caller or each call picks it?
Ask yourself “name or function?” Next lesson (2.2) builds on this:
constraints — how <T extends string> changes what TS infers for T.