# Typescript Patterns

> When to activate: TypeScript, type safety, generics, utility types, strict mode, type narrowing, discriminated unions, branded types

- Skill: `mattakushi432/typescript-patterns` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/typescript-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/typescript-patterns/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/typescript-patterns

---


# TypeScript Patterns

## tsconfig Baseline
```json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "moduleResolution": "bundler",
    "module": "ESNext",
    "target": "ES2022",
    "lib": ["ES2022", "DOM"],
    "paths": { "@/*": ["./src/*"] }
  }
}
```

## Utility Types
```typescript
// Pick, Omit, Partial, Required, Readonly
type CreateUser = Omit<User, 'id' | 'createdAt'>
type UpdateUser = Partial<Omit<User, 'id'>>
type ReadonlyUser = Readonly<User>

// ReturnType, Parameters, Awaited
type ApiResult = Awaited<ReturnType<typeof fetchUser>>
type FetchParams = Parameters<typeof fetchUser>[0]

// Record for index signatures
type FeatureFlags = Record<string, boolean>
type RouteHandlers = Record<string, (req: Request) => Response>

// Extract / Exclude for union manipulation
type StringOrNumber = string | number | boolean
type OnlyStrings = Extract<StringOrNumber, string>  // string
type NoStrings = Exclude<StringOrNumber, string>    // number | boolean
```

## Discriminated Unions
```typescript
type LoadingState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error }

function render<T>(state: LoadingState<T>) {
  switch (state.status) {
    case 'idle':    return null
    case 'loading': return '<Spinner />'
    case 'success': return state.data  // data is T here
    case 'error':   return state.error.message
  }
}
```

## Branded / Nominal Types
```typescript
declare const _brand: unique symbol
type Brand<T, B> = T & { [_brand]: B }

type UserId  = Brand<string, 'UserId'>
type OrderId = Brand<string, 'OrderId'>

const userId  = 'u_123' as UserId
const orderId = 'o_456' as OrderId

// Compile error: cannot pass OrderId where UserId expected
function getUser(id: UserId): User { ... }
getUser(orderId) // TS Error
```

## Generic Constraints
```typescript
// Constrain to objects with specific shape
function pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
  return keys.reduce((acc, k) => ({ ...acc, [k]: obj[k] }), {} as Pick<T, K>)
}

// Infer within conditionals
type Unpack<T> = T extends Array<infer U> ? U : T
type Item = Unpack<string[]>  // string
type Same = Unpack<string>    // string

// Template literal types
type EventName<T extends string> = `on${Capitalize<T>}`
type ClickHandler = EventName<'click'>  // 'onClick'
```

## Type Guards & Narrowing
```typescript
// Custom type guard
function isUser(v: unknown): v is User {
  return typeof v === 'object' && v !== null && 'id' in v && 'email' in v
}

// Assertion function
function assertDefined<T>(val: T | null | undefined, msg?: string): asserts val is T {
  if (val == null) throw new Error(msg ?? 'Expected defined value')
}

// instanceof narrowing
function handleError(err: unknown): string {
  if (err instanceof Error) return err.message
  if (typeof err === 'string') return err
  return 'Unknown error'
}
```

## Mapped Types
```typescript
// Make all properties nullable
type Nullable<T> = { [K in keyof T]: T[K] | null }

// Deep readonly
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}

// Conditional mapped types
type OptionalIfNullable<T> = {
  [K in keyof T as T[K] extends null | undefined ? K : never]?: T[K]
} & {
  [K in keyof T as T[K] extends null | undefined ? never : K]: T[K]
}
```

## Function Overloads
```typescript
function parse(input: string): number
function parse(input: number): string
function parse(input: string | number): string | number {
  if (typeof input === 'string') return Number(input)
  return String(input)
}
```

## Satisfies Operator (TS 4.9+)
```typescript
const palette = {
  red:   [255, 0, 0],
  green: '#00ff00',
} satisfies Record<string, string | number[]>

// palette.red is number[] (not string | number[])
palette.red.map(x => x * 2)  // OK — type is preserved
```

## Const Assertions
```typescript
const ROLES = ['admin', 'editor', 'viewer'] as const
type Role = (typeof ROLES)[number]  // 'admin' | 'editor' | 'viewer'

const config = {
  endpoint: 'https://api.example.com',
  timeout: 5000,
} as const
type Config = typeof config  // all properties readonly literals
```

## Error Handling Patterns
```typescript
// Result type without throwing
type Result<T, E = Error> =
  | { ok: true;  value: T }
  | { ok: false; error: E }

async function safeParseJson<T>(raw: string): Promise<Result<T>> {
  try {
    return { ok: true, value: JSON.parse(raw) as T }
  } catch (e) {
    return { ok: false, error: e instanceof Error ? e : new Error(String(e)) }
  }
}
```

## Module Augmentation
```typescript
// Extend Express Request
declare global {
  namespace Express {
    interface Request {
      user?: AuthUser
    }
  }
}

// Extend Window
interface Window {
  analytics: AnalyticsInstance
}
```

