TypeScript进阶

深入 TypeScript 的高级特性,掌握类型体操、条件类型、映射类型等进阶技巧,让你的类型系统更加强大。

高级类型

交叉类型

type Person = {
  name: string
  age: number
}

type Employee = {
  employeeId: string
  department: string
}

type EmployeePerson = Person & Employee

const employee: EmployeePerson = {
  name: '东巴文',
  age: 18,
  employeeId: 'E001',
  department: '技术部'
}

// 合并函数类型
type LogFunc = (msg: string) => void
type ErrorFunc = (err: Error) => void
type Logger = LogFunc & ErrorFunc

const logger: Logger = (input: string | Error) => {
  console.log(input)
}

联合类型

type ID = string | number
type Status = 'pending' | 'active' | 'inactive'

// 可辨识联合
interface Circle {
  kind: 'circle'
  radius: number
}

interface Rectangle {
  kind: 'rectangle'
  width: number
  height: number
}

interface Triangle {
  kind: 'triangle'
  base: number
  height: number
}

type Shape = Circle | Rectangle | Triangle

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2
    case 'rectangle':
      return shape.width * shape.height
    case 'triangle':
      return (shape.base * shape.height) / 2
  }
}

类型索引

type User = {
  id: number
  name: string
  email: string
  age: number
}

// 索引访问类型
type UserId = User['id']        // number
type UserName = User['name']    // string
type UserKeys = keyof User      // 'id' | 'name' | 'email' | 'age'

// 数组元素类型
type StringArrayElement = string[][number]  // string

// 嵌套索引
type Config = {
  database: {
    host: string
    port: number
  }
}

type DbHost = Config['database']['host']  // string

条件类型

基本语法

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

type A = IsString<string>  // true
type B = IsString<number>  // false

// 嵌套条件类型
type TypeName<T> = 
  T extends string ? 'string' :
  T extends number ? 'number' :
  T extends boolean ? 'boolean' :
  T extends undefined ? 'undefined' :
  T extends Function ? 'function' :
  'object'

type T1 = TypeName<string>    // 'string'
type T2 = TypeName<number[]>  // 'object'

条件类型约束

type MessageOf<T> = T extends { message: unknown } ? T['message'] : never

type EmailMessage = { message: string }
type ErrorMessage = { message: Error }

type Email = MessageOf<EmailMessage>  // string
type Error1 = MessageOf<ErrorMessage> // Error
type Empty = MessageOf<{}>            // never

// 约束条件类型
type Flatten<T> = T extends Array<infer U> ? U : T

type Str = Flatten<string[]>   // string
type Num = Flatten<number>     // number

infer 关键字

// 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never

type R1 = ReturnType<() => string>           // string
type R2 = ReturnType<(x: number) => boolean> // boolean

// 提取函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never

type P1 = Parameters<(a: string, b: number) => void>  // [string, number]

// 提取 Promise 值类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T

type Value = UnwrapPromise<Promise<string>>  // string

// 提取数组元素类型
type ElementOf<T> = T extends Array<infer E> ? E : never

type Elem = ElementOf<string[]>  // string

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

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

type UserParams = ConstructorParameters<typeof User>  // [string, number]

// 提取实例类型
type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : never

type UserInstance = InstanceType<typeof User>  // User

分布式条件类型

// 联合类型会分布式应用
type ToArray<T> = T extends any ? T[] : never

type StrArr = ToArray<string | number>  // string[] | number[]

// 避免分布式行为
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never

type Arr = ToArrayNonDist<string | number>  // (string | number)[]

映射类型

基本映射

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

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

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

type Mutable<T> = {
  -readonly [P in keyof T]: T[P]
}

// 使用
interface User {
  id: number
  name: string
  age?: number
}

type ReadonlyUser = Readonly<User>
type PartialUser = Partial<User>
type RequiredUser = Required<User>

映射修饰符

interface User {
  readonly id: number
  name: string
  age?: number
}

// 移除 readonly
type MutableUser = {
  -readonly [K in keyof User]: User[K]
}

// 移除可选
type RequiredUser = {
  [K in keyof User]-?: User[K]
}

// 添加可选
type OptionalUser = {
  [K in keyof User]+?: User[K]
}

键重映射

// 使用 as 重命名键
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}

interface Person {
  name: string
  age: number
}

type PersonGetters = Getters<Person>
// {
//   getName: () => string
//   getAge: () => number
// }

// 过滤键
type OnlyStrings<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K]
}

type StringProps = OnlyStrings<{ name: string; age: number; email: string }>
// { name: string; email: string }

模板字面量类型

type EventName = 'click' | 'focus' | 'blur'
type EventHandler = `on${Capitalize<EventName>}`
// 'onClick' | 'onFocus' | 'onBlur'

type HTTPMethod = 'get' | 'post' | 'put' | 'delete'
type APIEndpoint = `/api/${string}`
type Route = `${HTTPMethod} ${APIEndpoint}`
// 'get /api/...' | 'post /api/...' | ...

// 内置工具类型
type LowercaseName = Lowercase<'HELLO'>      // 'hello'
type UppercaseName = Uppercase<'hello'>      // 'HELLO'
type CapitalizeName = Capitalize<'hello'>    // 'Hello'
type UncapitalizeName = Uncapitalize<'Hello'> // 'hello'

工具类型实战

深度 Partial

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

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

type PartialConfig = DeepPartial<Config>

深度 Readonly

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

挑选和排除

// 按值类型挑选
type PickByType<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K]
}

interface User {
  name: string
  age: number
  email: string
  active: boolean
}

type StringProps = PickByType<User, string>
// { name: string; email: string }

type NumberProps = PickByType<User, number>
// { age: number }

// 按值类型排除
type OmitByType<T, U> = {
  [K in keyof T as T[K] extends U ? never : K]: T[K]
}

路径类型

type Path<T, K extends keyof T = keyof T> = 
  K extends string | number 
    ? T[K] extends object 
      ? K | `${K}.${Path<T[K]>}` 
      : K 
    : never

interface Config {
  server: {
    host: string
    port: number
  }
  db: {
    url: string
    options: {
      ssl: boolean
    }
  }
}

type ConfigPath = Path<Config>
// 'server' | 'server.host' | 'server.port' | 'db' | 'db.url' | 'db.options' | 'db.options.ssl'

联合转交叉

type UnionToIntersection<U> = 
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

type Union = { a: string } | { b: number }
type Intersection = UnionToIntersection<Union>
// { a: string } & { b: number }

联合转元组

type UnionToTuple<T, Last = LastOfUnion<T>> = 
  [T] extends [never] 
    ? [] 
    : [...UnionToTuple<Exclude<T, Last>>, Last]

type LastOfUnion<T> = 
  UnionToIntersection<T extends any ? () => T : never> extends () => infer R 
    ? R 
    : never

type Tuple = UnionToTuple<'a' | 'b' | 'c'>
// ['a', 'b', 'c']

类型体操

实现 Pick

type MyPick<T, K extends keyof T> = {
  [P in K]: T[P]
}

interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPreview = MyPick<Todo, 'title' | 'completed'>
// { title: string; completed: boolean }

实现 Omit

type MyOmit<T, K extends keyof T> = {
  [P in keyof T as P extends K ? never : P]: T[P]
}

type TodoWithoutDescription = MyOmit<Todo, 'description'>
// { title: string; completed: boolean }

实现 Readonly

type MyReadonly<T> = {
  readonly [P in keyof T]: T[P]
}

实现 Exclude

type MyExclude<T, U> = T extends U ? never : T

type Result = MyExclude<'a' | 'b' | 'c', 'a'>
// 'b' | 'c'

实现 Extract

type MyExtract<T, U> = T extends U ? T : never

type Result = MyExtract<'a' | 'b' | 'c', 'a' | 'b'>
// 'a' | 'b'

实现 NonNullable

type MyNonNullable<T> = T extends null | undefined ? never : T

type Result = MyNonNullable<string | null | undefined>
// string

实现 ReturnType

type MyReturnType<T extends (...args: any) => any> = 
  T extends (...args: any) => infer R ? R : never

function fn() {
  return { x: 10, y: 20 }
}

type R = MyReturnType<typeof fn>
// { x: number; y: number }

类型推导

递归类型

// 数组扁平化类型
type Flatten<T> = T extends Array<infer U> 
  ? U extends Array<any> 
    ? Flatten<U> 
    : U 
  : T

type Flat = Flatten<[1, [2, [3, [4]]]]>
// number

// 深度属性访问
type DeepValue<T, P extends string> = 
  P extends `${infer K}.${infer Rest}` 
    ? K extends keyof T 
      ? DeepValue<T[K], Rest> 
      : never 
    : P extends keyof T 
      ? T[P] 
      : never

interface Config {
  server: {
    host: string
    port: number
  }
}

type Host = DeepValue<Config, 'server.host'>  // string
type Port = DeepValue<Config, 'server.port'>  // number

类型推导实战

// 对象键值互换
type SwapKeyValue<T extends Record<string, string>> = {
  [K in keyof T as T[K]]: K
}

const colors = {
  red: '#ff0000',
  green: '#00ff00',
  blue: '#0000ff'
} as const

type ColorNames = SwapKeyValue<typeof colors>
// { '#ff0000': 'red'; '#00ff00': 'green'; '#0000ff': 'blue' }

// 提取对象方法
type Methods<T> = {
  [K in keyof T as T[K] extends Function ? K : never]: T[K]
}

class UserService {
  name = '东巴文'
  
  getName() { return this.name }
  setName(name: string) { this.name = name }
}

type UserServiceMethods = Methods<UserService>
// { getName: () => string; setName: (name: string) => void }

类型编程最佳实践

保持简单

// 过于复杂
type Complex<T> = T extends object 
  ? { [K in keyof T]: T[K] extends Function ? never : Complex<T[K]> }[keyof T]
  : T

// 简化
type Simplify<T> = T extends object 
  ? { [K in keyof T]: Simplify<T[K]> } 
  : T

使用工具类型

// 自定义工具类型库
type Utils = {
  DeepPartial: <T>(obj: T) => DeepPartial<T>
  DeepReadonly: <T>(obj: T) => DeepReadonly<T>
  RequiredKeys: <T>(obj: T) => { [K in keyof T]-?: T[K] }
  OptionalKeys: <T>(obj: T) => { [K in keyof T]+?: T[K] }
}

文档化复杂类型

/**
 * 深度可选类型
 * 递归地将所有属性变为可选
 * @template T - 要转换的类型
 * @example
 * type Config = DeepPartial<{ server: { host: string } }>
 * // { server?: { host?: string } }
 */
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
}

下一步

掌握 TypeScript 进阶后,你可以继续学习:


东巴文(db-w.cn)—— 让 TypeScript 更强大