webroad.online
  1. 1Web
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Tooling
  8. 8React
  9. 9State management
  10. 10Next.js
  11. 11Forms
  12. 12Data and backend
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Animations
  16. 16Testing
  17. 17Architecture
TypeScript · Lesson 4 of 6

Generics

Types with parameters, constraints with extends, keyof and T[K].

Updated

What generics are

Generics are types with parameters. Just as a function takes values as parameters, a generic takes types as parameters.

The problem they solve:

function first(arr: any[]): any { return arr[0] }
const n = first([1, 2])       // any — you lost the information that it's a number

function first<T>(arr: T[]): T | undefined { return arr[0] }
const m = first([1, 2])       // number ✓
const s = first(['a'])        // string ✓

T is a type parameter — a blank filled in automatically by TS from the arguments. There's still just one function, but the result's type follows the input's type.

You already use them

const ids: Array<number> = []                 // = number[]
const users = new Map<string, User>()
const [count, setCount] = useState<number>(0)
const res: Promise<User> = getUser()

Constraints with extends

Sometimes T can't be just anything — it has to have something specific:

function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b
}

longest('abc', 'de')        // ✓ a string has length
longest([1, 2], [3])        // ✓ an array has length
longest(10, 20)             // ✗ a number has no length

keyof and access by key

function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
  return items.map(item => item[key])
}

pluck(users, 'email')       // string[]
pluck(users, 'emial')       // ✗ typo caught

keyof T = the union of T's keys; T[K] = the type of the value at key K.

Generic types

It's not only functions that can be generic:

type ApiResponse<T> =
  | { ok: true; data: T }
  | { ok: false; error: string }

type Paginated<T> = { items: T[]; page: number; total: number }

const res: ApiResponse<Paginated<Product>> = await api('/products')

When to use generics

Situation Generic?
a function that works the same with any type (first, groupBy, pluck) yes
a "container" type (an API response, pagination, form state) yes
a reusable React component for any item type (List<T>, Select<T>) yes
a function that only works with User no — write User

A sign you're overdoing it: T appears only once in the signature. Then there's no need for a generic.

Summary

  • A generic = a type with a parameter; it keeps the link between the input type and the output type.
  • extends enforces what T must have; keyof T + T[K] for access by key.
  • Use them for utilities and containers, not everywhere.

Official sources

Exercises

Was this page helpful?

One tap — no account needed.