C.W.K.
Stream
Lesson 01 of 07 · published

Why Generics: Types That Hold a Slot

~10 min · generics, intro, type-parameters

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"A generic is a type that hasn't decided yet. The caller fills in the slot."

The problem generics solve

Imagine you're writing a function first that returns the first element of an array. Without generics, you'd write one per element type — firstString, firstNumber, firstUser — or you'd accept any[] and return any, losing all type safety. Both options are bad.

Generics solve this by letting you parameterize the function over an unknown type, captured at the call site. function first<T>(arr: T[]): T — one function, one signature. The compiler figures out what T is from the argument and threads it through to the return type.

The slot metaphor

Think of <T> as a slot in the signature. The caller (or the compiler, via inference) fills the slot when the function is invoked. first([1, 2, 3]) fills T with number; first(['a', 'b']) fills T with string. The same function code runs in both cases; the type system tracks the difference.

This is what Array<T> is: an array type with a slot for the element type. Array<string> is string[]. Array<{ id: number }> is an array of objects. The same shape, different fills.

The relationship-preservation property

The killer feature of generics is that the slot is the same throughout the signature. If T appears in both a parameter and a return, the call enforces they're the same type. function identity<T>(x: T): T — whatever you put in is exactly what comes back, with the type preserved. This relationship-tracking is impossible with primitive types alone.

Generics aren't a special TypeScript feature — they're how typed collections, typed Promises, typed mappers, and typed callbacks all work. Every Array, every Map, every Promise has at least one type parameter. Learning generics isn't optional; it's how the standard library is shaped.

Code

Generic basics — slot, fill, relationship·typescript
// Without generics — losing the type.
function firstLoose(arr: any[]): any {
  return arr[0];
}
const a = firstLoose([1, 2, 3]);   // a: any — no help

// With generics — relationship preserved.
function first<T>(arr: T[]): T {
  return arr[0];
}
const b = first([1, 2, 3]);        // b: number — inferred
const c = first(['a', 'b']);       // c: string
const d = first<boolean>([true]);  // d: boolean — explicit

// identity — the textbook generic.
function identity<T>(x: T): T { return x }
const e = identity(42);            // e: number
const f = identity('hi');          // f: string

External links

Exercise

Write a generic swap function that takes a tuple [T, U] and returns [U, T]. Notice how the function uses two type parameters and the relationship between input and output. Then try swap([1, 'two']) and swap([true, { x: 1 }]) — confirm the return type is what you expect.
Hint
function swap<T, U>(p: [T, U]): [U, T] { return [p[1], p[0]] }. The compiler infers T and U from the input tuple and threads them through to the return.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.