Skip to content

πŸ”· TypeScript Patterns ​

Leveraging TypeScript's type system for safer, more expressive code.

Essential types ​

typescript
// Union types
type Status = 'pending' | 'active' | 'archived'

// Discriminated unions
type Result<T> =
  | { ok: true; data: T }
  | { ok: false; error: string }

// Utility types
Partial<T>       // All properties optional
Required<T>      // All properties required
Pick<T, K>       // Select specific properties
Omit<T, K>       // Exclude specific properties
Record<K, V>     // Object with typed keys and values

Type narrowing ​

typescript
function handle(input: string | number) {
  if (typeof input === 'string') {
    // TypeScript knows input is string here
    return input.toUpperCase()
  }
  return input * 2
}

Generics ​

typescript
// Generic function
function first<T>(arr: T[]): T | undefined {
  return arr[0]
}

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

Zod for runtime validation ​

typescript
import { z } from 'zod'

const UserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().positive().optional(),
})

type User = z.infer<typeof UserSchema>

// Validates at runtime AND infers types
const user = UserSchema.parse(requestBody)

Best practices ​

  • Prefer type over interface for unions and intersections
  • Use interface for extendable contracts (classes, APIs)
  • Avoid any β€” use unknown and narrow
  • Use as const for literal types
  • Enable strict mode in tsconfig
  • Don't over-type β€” let TypeScript infer when it can

Anti-patterns ​

  • any everywhere (defeats the purpose)
  • Type assertions (as) to silence errors instead of fixing them
  • Excessively deep generic types that are hard to read
  • Duplicating types instead of deriving them

Pergame Knowledge Base