All Resources
Guide
TypeScriptAdvancedType System
June 19, 2024

TypeScript Generics Deep Dive

Master TypeScript generics for writing flexible, reusable, type-safe code.

Overview

Generics allow you to write code that works with different types while maintaining full type safety. Master this advanced feature for powerful abstractions.

Concepts

  • Type Variables: Capture types as parameters
  • Constraints: Limit what types can be passed
  • Inference: TypeScript automatically figures out types
  • Default Types: Provide fallback types
  • Variance: Understand type relationships

Basic Generics

// Generic function
function identity<T>(arg: T): T {
  return arg
}

const num = identity<number>(42)
const str = identity<string>('hello')

// Inferred types
const str2 = identity('world') // T inferred as string

Generic Constraints

interface HasLength {
  length: number
}

// Type must have 'length' property
function getLength<T extends HasLength>(arg: T): number {
  return arg.length
}

getLength('hello') // ✓ strings have length
getLength([1, 2, 3]) // ✓ arrays have length
getLength(123) // ✗ numbers don't have length

Generic with keyof

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

interface User {
  id: number
  name: string
}

const user: User = { id: 1, name: 'Alice' }

const id = getProperty(user, 'id') // Type: number
const name = getProperty(user, 'name') // Type: string
getProperty(user, 'email') // ✗ Type error: 'email' not in User

Generic Classes

class Stack<T> {
  private items: T[] = []

  push(item: T): void {
    this.items.push(item)
  }

  pop(): T | undefined {
    return this.items.pop()
  }

  peek(): T | undefined {
    return this.items[this.items.length - 1]
  }
}

const numberStack = new Stack<number>()
numberStack.push(1)
numberStack.push(2)
const value = numberStack.pop() // Type: number | undefined

Conditional Generic Types

type IsString<T> = T extends string ? true : false

type A = IsString<'hello'> // true
type B = IsString<number> // false

// Practical example
type Flatten<T> = T extends Array<infer U> ? U : T

type Str = Flatten<string[]> // string
type Num = Flatten<number> // number

Generics reduce code duplication by 50% while maintaining type safety.

Curated by

Shyam