Skip to content
AskSoul

TypeScript 高级类型技巧

概述

TypeScript 的高级类型系统是其最强大的特性之一。本文深入探索 TypeScript 高级类型、条件类型、映射类型等高级特性,帮助你构建更健壮的类型定义。

条件类型

基本语法

typescript
type TypeA = SomeType extends OtherType ? TrueType : FalseType

示例

typescript
type IsString<T> = T extends string ? true : false

type A = IsString<"hello">  // true
type B = IsString<123>      // false

分布式条件类型

typescript
type ToArray<T> = T extends any ? T[] : never

type A = ToArray<string>        // string[]
type B = ToArray<number | string>  // number[] | string[]

条件类型推断

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

type A = ReturnType<() => string>      // string
type B = ReturnType<() => Promise<number>>  // Promise<number>
type C = ReturnType<() => never>       // never

type Parameters<T> = T extends (...args: infer P) => any ? P : never

type A = Parameters<(name: string, age: number) => void>  // [string, number]

映射类型

基本语法

typescript
type MappedType<T> = {
  [K in keyof T]: T[K]
}

只读和可选

typescript
type Readonly<T> = {
  readonly [K in keyof T]: T[K]
}

type Partial<T> = {
  [K in keyof T]?: T[K]
}

type Required<T> = {
  [K in keyof T]-?: T[K]
}

实践示例

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

type ReadonlyUser = Readonly<User>
type PartialUser = Partial<User>
type UserPreview = Pick<User, 'id' | 'name'>
type UserUpdate = Partial<Pick<User, 'name' | 'email'>>

键重新映射

typescript
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}

interface User {
  name: string
  age: number
}

type UserGetters = Getters<User>
// { getName: () => string; getAge: () => number }

模板字面量类型

基本用法

typescript
type World = "world"
type Greeting = `hello ${World}`  // "hello world"

type EmailLocaleIDs = "welcome-email" | "email-heading"
type FooterLocaleIDs = "footer-title" | "footer-sendoff"
type AllLocaleIDs = `${EmailLocaleIDs}_id` | `${FooterLocaleIDs}_id`
// "welcome-email_id" | "email-heading_id" | "footer-title_id" | "footer-sendoff_id"

提取路径

typescript
type ExtractRouteParams<T extends string> =
  T extends `${infer _Start}:${infer Param}/${infer Rest}`
    ? Param | ExtractRouteParams<`/${Rest}`>
    : T extends `${infer _Start}:${infer Param}`
    ? Param
    : never

type Params = ExtractRouteParams<'/users/:id/posts/:postId'>
// "id" | "postId"

递归类型

深度只读

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

interface Nested {
  user: {
    name: string
    address: {
      city: string
    }
  }
}

type DeepNested = DeepReadonly<Nested>
// 所有嵌套属性都变为 readonly

深度可选

typescript
type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}

展平类型

typescript
type Flatten<T> = T extends Array<infer U>
  ? U extends object
    ? Flatten<U>
    : U
  : T

type A = Flatten<string[]>  // string
type B = Flatten<number[][]>  // number
type C = Flatten<{ a: string }>  // { a: string }

infer 关键字

在条件类型中推断

typescript
// 推断函数返回类型
type UnpackPromise<T> = T extends Promise<infer U> ? U : T

type A = UnpackPromise<Promise<string>>  // string
type B = UnpackPromise<Promise<number[]>>  // number[]

// 推断构造函数
type ConstructorParameters<T extends new (...args: any) => any> =
  T extends new (...args: infer P) => any ? P : never

class Person {
  constructor(public name: string, public age: number) {}
}

type PersonParams = ConstructorParameters<typeof Person>  // [string, number]

工具类型扩展

自定义工具类型

typescript
// 排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T

// 移除索引签名
type RemoveIndexSignature<T> = {
  [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]
}

// 私有属性
type Public<T> = {
  [K in keyof T as K extends `_${string}` ? never : K]: T[K]
}

互斥属性

typescript
type Exclusive<T, U extends keyof T> = T & { [K in Exclude<keyof T, U>]?: never }

type User = {
  name: string
  email: string
}

type GuestUser = Exclusive<User, 'email'>
// { name: string; email: string; password?: never } | { name: string; email?: never }

高级模式

链式调用类型

typescript
type Chainable<T = {}> = {
  option<K extends string, V>(
    key: K,
    value: V
  ): Chainable<T & { [P in K]: V }>
  get(): T
}

declare function chainable<T>(): Chainable<T>

const result = chainable()
  .option('name', 'John')
  .option('age', 30)
  .get()
// result: { name: string; age: number }

事件系统

typescript
type EventName<T extends string> = `on${Capitalize<T>}`

type EventHandlers<T extends string> = {
  [K in EventName<T>]: (event: CustomEvent) => void
}

type ButtonEvents = EventHandlers<'click' | 'focus' | 'blur'>
// { onClick: (event: CustomEvent) => void; onFocus: ...; onBlur: ... }

类型安全的 EventEmitter

typescript
type EventMap = {
  user: { id: number; name: string }
  post: { id: number; title: string }
  comment: { id: number; content: string }
}

class TypedEventEmitter<Events extends Record<string, any>> {
  on<K extends keyof Events>(event: K, listener: (data: Events[K]) => void): this {
    // implementation
    return this
  }

  emit<K extends keyof Events>(event: K, data: Events[K]): boolean {
    // implementation
    return true
  }
}

const emitter = new TypedEventEmitter<EventMap>()

emitter.on('user', (data) => {
  console.log(data.id, data.name)  // 类型安全
})

实用技巧

类型守卫组合

typescript
type isStringOrNumber<T> = T extends string ? 'string' : 'number'

type A = isStringOrNumber<string>  // 'string'
type B = isStringOrNumber<number>  // 'number'

type IsUnion<T, U = T> = T extends U
  ? [U] extends [T]
    ? false
    : true
  : false

type C = IsUnion<string | number>  // true
type D = IsUnion<string>  // false

Variance 标注

typescript
// 协变 (Covariance) - 返回类型
type Covariant<T> = () => T

// 逆变 (Contravariance) - 参数类型
type Contravariant<T> = (value: T) => void

// 不变 (Invariant) - 既做参数又做返回
type Invariant<T> = (value: T) => T

避免类型膨胀

typescript
// 使用泛型约束
type Response<T> = {
  data: T
  status: number
  message: string
}

// 使用映射类型
type AllResponses = {
  [K in keyof typeof endpoints]: Response<(typeof endpoints)[K]>
}

总结

掌握 TypeScript 高级类型能够:

  • 构建更精确的类型定义
  • 提高代码的类型安全性
  • 提供更好的开发时类型提示
  • 编写更灵活和可复用的代码