Lesson 13 · Module 2.1 — Generics

Declaring a generic: the <T> placeholder and where it lives

A 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.

The one idea

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.

“Type variable” and “type parameter” are the same thing

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 positions where you declare one

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.

The one distinction that trips readers: generic function type vs parameterized alias

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.

Check yourself

Recall, don’t re-read. (Answers reveal on click.)

In function identity<T>(arg: T): T, the <T> is best read as…
For type Predicate<T> = (value: T) => boolean, the type parameter T is…
Which line declares a generic function type whose T resolves per call?

Primary source

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>.

Try it yourself

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.