Back to Blog

Advanced TypeScript Patterns for Better Code

Explore advanced TypeScript patterns and techniques that will make your code more type-safe, maintainable, and self-documenting.

12-14 minutes
Intermediate to Senior Engineers, Full-Stack Developers, TypeScript Architects

Overview

TypeScript is more than just adding types — it's a language for building self-validating, maintainable code. In this guide, we'll dive into advanced TypeScript patterns that help improve type safety, enforce business rules at compile time, and make your codebase easier to understand and scale.

Why Advanced TypeScript?

Advanced TypeScript techniques give you tools to write safer and cleaner code, especially in large-scale applications. By leveraging the type system, you can reduce runtime bugs, create more expressive APIs, and improve developer experience through auto-complete and static validation.

1. Utility Types Deep Dive

Partial<T>

TypeScript Utility

Make all properties in T optional

type User = { id: number; name: string }
type UserUpdate = Partial<User>

Pick<T, K>

TypeScript Utility

Select a subset of keys

type UserPreview = Pick<User, 'id' | 'name'>

Record<K, T>

TypeScript Utility

Create a map-like object with consistent value types

type StatusMap = Record<'loading' | 'success' | 'error', boolean>

Required<T>

TypeScript Utility

Make all optional properties required

type StrictUser = Required<Partial<User>>

2. Conditional Types

TypeScript
type IsString<T> = T extends string ? 'yes' : 'no'

// Usage:
type A = IsString<string> // 'yes'
type B = IsString<number> // 'no'

Real-world Use Case:

Building API response types that change based on status

3. Discriminated Unions

TypeScript
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; side: number }

function getArea(shape: Shape) {
  if (shape.kind === 'circle') {
    return Math.PI * shape.radius ** 2
  } else {
    return shape.side ** 2
  }
}

4. Template Literal Types

TypeScript
type Routes = `/${'home' | 'about' | 'contact'}`

const route: Routes = '/about' // ✅

5. Mapped Types + Key Remapping

TypeScript
type APIResponse<T> = {
  [K in keyof T as `api_${string & K}`]: T[K]
}

// Usage:
type Original = { name: string; age: number }
type Remapped = APIResponse<Original> // { api_name: string; api_age: number }

6. Infer Keyword for Type Extraction

TypeScript
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never

function getUser() {
  return { id: 1, name: 'Alice' }
}
type User = GetReturnType<typeof getUser> // { id: number; name: string }

Use Case:

Extract types from functions or Promises without duplicating them

7. Advanced Generics with Constraints

TypeScript
function merge<T extends object, U extends object>(a: T, b: U): T & U {
  return { ...a, ...b }
}

const result = merge({ name: 'A' }, { age: 30 })

💡 Tip:

Constrain generics to prevent misuse and improve IntelliSense

8. Type Guards and Type Predicates

TypeScript
function isString(val: unknown): val is string {
  return typeof val === 'string'
}

function print(val: unknown) {
  if (isString(val)) {
    console.log(val.toUpperCase())
  }
}

9. Branded Types (Nominal Typing)

TypeScript
type UserID = string & { __brand: 'UserID' }

function createUser(id: UserID) { /* ... */ }

const id = 'abc123' as UserID // Valid branded ID

Use Case:

Prevent mixing of similar types (e.g. string vs UserID) in large codebases

10. Self-Documenting API Types

Use TypeScript to encode logic into your types. When types are expressive enough, they become documentation for other developers and prevent incorrect usage.

Example:

TypeScript
type CreateUserOptions = {
  email: string
  password: string
  confirmPassword: string
}

function createUser({ email, password, confirmPassword }: CreateUserOptions) {
  if (password !== confirmPassword) throw new Error('Passwords must match')
}

Final Checklist for TypeScript Mastery

Checklist:

  • ✅ Use `Partial`, `Pick`, and `Record` to reduce duplication
  • ✅ Leverage `infer`, `extends`, and conditional types for reusable logic
  • ✅ Use discriminated unions for safe polymorphism
  • ✅ Embrace template literal and mapped types to auto-generate structure
  • ✅ Write type guards to safely narrow unknown types
  • ✅ Use branded types to prevent accidental misuse

Closing Thoughts

Advanced TypeScript isn't just about complexity — it's about clarity, safety, and scalability. By mastering these patterns, you create code that is more reliable, easier to understand, and future-proof. Treat your types as part of your product, and you'll reap the rewards in team productivity and user safety.

Thanks for reading! Found this helpful?

Read More Articles