TypeScript Advanced Type System: Mastering Generics and Type Gymnastics
TypeScript Advanced Type System: Mastering Generics and Type Gymnastics
TypeScript's type system is one of its most powerful features. This comprehensive guide explores advanced type techniques including generics, conditional types, mapped types, and type gymnastics that will elevate your TypeScript skills.
Why Master Advanced Types?
- Compile-Time Safety: Catch errors before runtime
- Better Developer Experience: Enhanced IDE autocomplete
- Self-Documenting Code: Types serve as living documentation
- Refactoring Confidence: Safe large-scale code changes
1. Generics Fundamentals
1.1 Basic Generic Functions
function identity<T>(arg: T): T {
return arg
}
const str = identity<string>('hello')
const num = identity(42) // Type inference works!
// Generic with multiple parameters
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second]
}
const result = pair('hello', 42) // [string, number]
1.2 Generic Constraints
interface Lengthwise {
length: number
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length)
return arg
}
logLength('hello') // ✅ string has length
logLength([1, 2, 3]) // ✅ array has length
logLength(42) // ❌ Error: number has no length
// Keyof constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
const user = { name: 'Alice', age: 30 }
const name = getProperty(user, 'name') // Type: string
const age = getProperty(user, 'age') // Type: number
1.3 Generic Classes
class DataStore<T> {
private data: T[] = []
add(item: T): void {
this.data.push(item)
}
get(index: number): T | undefined {
return this.data[index]
}
getAll(): T[] {
return [...this.data]
}
}
const numberStore = new DataStore<number>()
numberStore.add(1)
numberStore.add(2)
const stringStore = new DataStore<string>()
stringStore.add('hello')
2. Conditional Types
2.1 Basic Syntax
type IsString<T> = T extends string ? true : false
type A = IsString<string> // true
type B = IsString<number> // false
// Practical example: Extract array element type
type Flatten<T> = T extends Array<infer U> ? U : T
type Str = Flatten<string[]> // string
type Num = Flatten<number> // number
2.2 Distributive Conditional Types
type ToArray<T> = T extends any ? T[] : never
// Distributes over union types
type Arrays = ToArray<string | number>
// Result: string[] | number[]
// Filter union types
type Filter<T, U> = T extends U ? T : never
type Filtered = Filter<string | number | boolean, string | number>
// Result: string | number
2.3 The infer Keyword
// Extract return type
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never
function getUser() {
return { name: 'Alice', age: 30 }
}
type User = GetReturnType<typeof getUser>
// { name: string; age: number }
// Unwrap Promise
type Awaited<T> = T extends Promise<infer U> ? U : T
type Data = Awaited<Promise<string>> // string
// Extract parameters
type GetParameters<T> = T extends (...args: infer P) => any ? P : never
function createPost(title: string, content: string) {
return { title, content }
}
type Params = GetParameters<typeof createPost>
// [string, string]
3. Mapped Types
3.1 Built-in Mapped Types
interface User {
id: number
name: string
email: string
}
// Partial: All properties optional
type PartialUser = Partial<User>
// { id?: number; name?: string; email?: string }
// Required: All properties required
type RequiredUser = Required<PartialUser>
// Readonly: All properties readonly
type ReadonlyUser = Readonly<User>
// Pick: Select specific properties
type UserPreview = Pick<User, 'id' | 'name'>
// { id: number; name: string }
// Omit: Exclude specific properties
type UserWithoutEmail = Omit<User, 'email'>
// { id: number; name: string }
3.2 Custom Mapped Types
// Add prefix to all keys
type Prefixed<T, P extends string> = {
[K in keyof T as `${P}${string & K}`]: T[K]
}
interface Person {
name: string
age: number
}
type PrefixedPerson = Prefixed<Person, 'user_'>
// { user_name: string; user_age: number }
// Filter by value type
type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K]
}
interface Mixed {
id: number
name: string
age: number
active: boolean
}
type Strings = PickByType<Mixed, string> // { name: string }
type Numbers = PickByType<Mixed, number> // { id: number; age: number }
3.3 Template Literal Types
type EventName = 'click' | 'focus' | 'blur'
type EventHandler = `on${Capitalize<EventName>}`
// "onClick" | "onFocus" | "onBlur"
// Combine multiple template literals
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
type Endpoint = '/users' | '/posts'
type APIRoute = `${HTTPMethod} ${Endpoint}`
// "GET /users" | "POST /users" | "GET /posts" | ...
// CSS properties
type CSSUnit = 'px' | 'em' | 'rem' | '%'
type Size = `${number}${CSSUnit}`
const width: Size = '100px' // ✅
const height: Size = '2em' // ✅
const invalid: Size = '100' // ❌
4. Utility Types
4.1 Object Utilities
// Record: Create object type
type PageConfig = Record<'home' | 'about' | 'contact', {
title: string
description: string
}>
const config: PageConfig = {
home: { title: 'Home', description: 'Welcome' },
about: { title: 'About', description: 'About us' },
contact: { title: 'Contact', description: 'Get in touch' }
}
// Exclude: Remove from union
type T1 = Exclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
// Extract: Keep only matching
type T2 = Extract<'a' | 'b' | 'c', 'a' | 'b'> // 'a' | 'b'
// NonNullable: Remove null/undefined
type T3 = NonNullable<string | null | undefined> // string
4.2 Function Utilities
// ReturnType
function createUser(name: string, age: number) {
return { id: Math.random(), name, age }
}
type User = ReturnType<typeof createUser>
// { id: number; name: string; age: number }
// Parameters
type CreateUserParams = Parameters<typeof createUser>
// [string, number]
// ConstructorParameters
class Person {
constructor(public name: string, public age: number) {}
}
type PersonParams = ConstructorParameters<typeof Person>
// [string, number]
// InstanceType
type PersonInstance = InstanceType<typeof Person> // Person
5. Advanced Type Patterns
5.1 Deep Readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: DeepReadonly<T[P]>
: T[P]
}
interface Config {
database: {
host: string
port: number
}
}
type ReadonlyConfig = DeepReadonly<Config>
// All nested properties are readonly
5.2 Type-Safe Path Access
type PathKeys<T> = {
[K in keyof T]: T[K] extends object
? K | `${K & string}.${PathKeys<T[K]> & string}`
: K
}[keyof T]
interface Data {
user: {
profile: {
name: string
age: number
}
}
}
type Paths = PathKeys<Data>
// "user" | "user.profile" | "user.profile.name" | "user.profile.age"
6. Real-World Applications
6.1 API Response Types
Perfect for React 19:
type ApiResponse<T> =
| { success: true; data: T }
| { success: false; error: { code: string; message: string } }
async function fetchUser(id: number): Promise<ApiResponse<User>> {
try {
const response = await fetch(`/api/users/${id}`)
const data = await response.json()
return { success: true, data }
} catch (error) {
return {
success: false,
error: { code: 'FETCH_ERROR', message: 'Failed to fetch user' }
}
}
}
// Type guard
function isSuccess<T>(res: ApiResponse<T>): res is { success: true; data: T } {
return res.success
}
// Usage
const result = await fetchUser(1)
if (isSuccess(result)) {
console.log(result.data.name) // ✅ Type-safe
}
6.2 Form State Management
type FormField<T> = {
value: T
error?: string
touched: boolean
}
type FormState<T> = {
[K in keyof T]: FormField<T[K]>
}
interface LoginForm {
email: string
password: string
}
type LoginState = FormState<LoginForm>
// {
// email: FormField<string>
// password: FormField<string>
// }
6.3 Redux-Style Actions
type Action<T extends string, P = void> = P extends void
? { type: T }
: { type: T; payload: P }
type UserAction =
| Action<'LOGIN', { email: string; token: string }>
| Action<'LOGOUT'>
| Action<'UPDATE', Partial<User>>
function reducer(state: UserState, action: UserAction): UserState {
switch (action.type) {
case 'LOGIN':
return { ...state, ...action.payload }
case 'LOGOUT':
return initialState
case 'UPDATE':
return { ...state, ...action.payload }
}
}
7. Performance Tips
// ❌ Avoid: Over-complex types
type Bad<T> = T extends A ? T extends B ? T extends C ? D : E : F : G
// ✅ Better: Split into steps
type Step1<T> = T extends A ? T : never
type Step2<T> = Step1<T> extends B ? T : never
type Good<T> = Step2<T> extends C ? D : E
// Use type aliases for reusability
type ProcessedData = DeepReadonly<DeepPartial<ComplexType>>
8. Integration Examples
8.1 Next.js API Routes
Works with Next.js 15:
import { NextRequest, NextResponse } from 'next/server'
interface CreatePostBody {
title: string
content: string
}
interface CreatePostResponse {
success: boolean
postId?: string
}
export async function POST(
request: NextRequest
): Promise<NextResponse<CreatePostResponse>> {
const body: CreatePostBody = await request.json()
// Type-safe implementation
}
8.2 Database Queries
Works with PostgreSQL:
interface User {
id: number
name: string
email: string
}
type QueryResult<T> = Promise<{ rows: T[]; rowCount: number }>
async function findUsers(): QueryResult<User> {
return db.query('SELECT * FROM users')
}
9. Best Practices
- ✅ Enable
strict: truein tsconfig.json - ✅ Use type inference when possible
- ✅ Prefer
unknownoverany - ✅ Write type guards for runtime checks
- ✅ Keep types simple and readable
- ✅ Use utility types from TypeScript
- ✅ Split complex types into smaller ones
- ✅ Document complex type logic
Related Resources
- React 19 Complete Guide
- Next.js 15 App Router Guide
- PostgreSQL Optimization
- Docker Containerization
- Web Performance
Conclusion
TypeScript's advanced type system enables building robust, maintainable applications. Master generics for reusability, conditional types for flexibility, and mapped types for transformations. The goal is type safety and developer experience, not complexity. Start simple and gradually adopt advanced patterns as needed.
Comments
Share your thoughts and join the discussion
Comments (0)
Related Articles
TypeScript 高级类型技巧:提升代码质量
学习 TypeScript 的高级类型系统,包括泛型、条件类型、映射类型等,让你的代码更加类型安全和可维护。本文通过实际案例展示如何在项目中应用这些高级特性。
Docker Containerization Best Practices: Complete Guide to Production-Ready Containers
Master Docker containerization with multi-stage builds, security hardening, Docker Compose orchestration, and production deployment strategies. Learn how to build efficient, secure, and scalable containerized applications.
PostgreSQL Performance Optimization: Complete Guide to Database Tuning
Master PostgreSQL performance optimization with indexing strategies, query tuning, EXPLAIN analysis, VACUUM operations, connection pooling, and partitioning. Learn practical techniques to scale your database for high-traffic applications.
Please or to comment