Advanced TypeScript Patterns for Better Code
Explore advanced TypeScript patterns and techniques that will make your code more type-safe, maintainable, and self-documenting.
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 UtilityMake all properties in T optional
type User = { id: number; name: string }
type UserUpdate = Partial<User>
Pick<T, K>
TypeScript UtilitySelect a subset of keys
type UserPreview = Pick<User, 'id' | 'name'>
Record<K, T>
TypeScript UtilityCreate a map-like object with consistent value types
type StatusMap = Record<'loading' | 'success' | 'error', boolean>
Required<T>
TypeScript UtilityMake all optional properties required
type StrictUser = Required<Partial<User>>
2. Conditional Types
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
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
type Routes = `/${'home' | 'about' | 'contact'}`
const route: Routes = '/about' // ✅
5. Mapped Types + Key Remapping
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
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
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
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)
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:
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