Back to Blog
TypeScriptJavaScriptProgramming

Mastering TypeScript: Advanced Patterns for Better Code

Deep dive into advanced TypeScript patterns including conditional types, mapped types, and template literal types.

Alex Chen
January 10, 2024
12 min read

# Advanced TypeScript Patterns

TypeScript has evolved into a powerful type system that goes far beyond basic type annotations. In this deep dive, we'll explore advanced patterns that can help you write more robust and maintainable code.

## Conditional Types

Conditional types allow you to create types that depend on a condition:

```typescript
type ApiResponse = T extends string
? { message: T }
: { data: T }

// Usage
type StringResponse = ApiResponse // { message: string }
type NumberResponse = ApiResponse // { data: number }
```

### Practical Example: Safe Array Access

```typescript
type SafeGet = K extends keyof T ? T[K] : never

interface User {
id: number
name: string
email: string
}

type UserName = SafeGet // string
type Invalid = SafeGet // never
```

## Mapped Types

Mapped types let you create new types by transforming properties of existing types:

```typescript
type Partial = {
[P in keyof T]?: T[P]
}

type Required = {
[P in keyof T]-?: T[P]
}

type Readonly = {
readonly [P in keyof T]: T[P]
}
```

### Custom Mapped Types

```typescript
type Stringify = {
[K in keyof T]: string
}

type Promisify = {
[K in keyof T]: Promise
}

interface User {
id: number
name: string
active: boolean
}

type StringUser = Stringify
// { id: string; name: string; active: string }

type AsyncUser = Promisify
// { id: Promise; name: Promise; active: Promise }
```

## Template Literal Types

Template literal types enable powerful string manipulation at the type level:

```typescript
type EventName = `on${Capitalize}`

type ClickEvent = EventName<'click'> // 'onClick'
type HoverEvent = EventName<'hover'> // 'onHover'
```

### Building a Type-Safe Event System

```typescript
type EventMap = {
click: { x: number; y: number }
hover: { element: HTMLElement }
scroll: { top: number; left: number }
}

type EventHandler = (
event: EventMap[T]
) => void

type EventHandlers = {
[K in keyof EventMap as `on${Capitalize}`]?: EventHandler
}

// Result:
// {
// onClick?: (event: { x: number; y: number }) => void
// onHover?: (event: { element: HTMLElement }) => void
// onScroll?: (event: { top: number; left: number }) => void
// }
```

## Utility Types in Action

### Deep Readonly

```typescript
type DeepReadonly = {
readonly [P in keyof T]: T[P] extends object
? DeepReadonly
: T[P]
}

interface Config {
database: {
host: string
port: number
credentials: {
username: string
password: string
}
}
}

type ReadonlyConfig = DeepReadonly
// All properties and nested properties are readonly
```

### Type-Safe Object Paths

```typescript
type Paths = T extends object
? {
[K in keyof T]: K extends string
? T[K] extends object
? K | `${K}.${Paths}`
: K
: never
}[keyof T]
: never

type UserPaths = Paths
// 'id' | 'name' | 'email' | 'profile' | 'profile.bio' | 'profile.avatar'
```

## Advanced Function Types

### Function Overloads with Conditional Types

```typescript
interface Database {
get(type: T, id: string): Promise
get(type: T, id: string): Promise
get(type: T, id: string): Promise
}

// Or using conditional types:
type GetReturnType =
T extends 'user' ? User :
T extends 'post' ? Post :
T extends 'comment' ? Comment :
never

interface Database {
get(
type: T,
id: string
): Promise>
}
```

## Best Practices

1. **Start Simple**: Don't over-engineer types from the beginning
2. **Use Type Guards**: Combine runtime checks with type narrowing
3. **Leverage Utility Types**: Use built-in utilities before creating custom ones
4. **Document Complex Types**: Add comments for complex type logic
5. **Test Your Types**: Use type-level tests to ensure correctness

## Conclusion

Advanced TypeScript patterns unlock powerful capabilities for creating type-safe, maintainable applications. While these patterns might seem complex at first, they become invaluable tools for building robust software systems.

The key is to gradually incorporate these patterns into your codebase, starting with simpler use cases and building up to more complex scenarios as your understanding grows.

---

*Have you used any of these advanced TypeScript patterns in your projects? Share your experiences in the comments!*