Skip to content

泛型深度解析

泛型是 TypeScript 最强大的特性,让你写出既类型安全又高度复用的代码。

泛型约束

ts
// extends 约束:T 必须有 length 属性
function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b
}

longest('hello', 'hi')        // ✓ string 有 length
longest([1, 2, 3], [1, 2])    // ✓ array 有 length
longest({ length: 5 }, { length: 3 })  // ✓

// 多个约束
function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 }
}

// keyof 约束
function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
  return items.map(item => item[key])
}

const users = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]
pluck(users, 'name')  // string[]
pluck(users, 'age')   // number[]
// pluck(users, 'email')  // ❌ 编译错误

条件类型

ts
// 基本条件类型
type IsString<T> = T extends string ? true : false

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

// infer:在条件类型中推断类型
type UnpackPromise<T> = T extends Promise<infer U> ? U : T
type UnpackArray<T>   = T extends Array<infer U>   ? U : T

type R1 = UnpackPromise<Promise<string>>  // string
type R2 = UnpackPromise<number>           // number(不是 Promise,原样返回)
type R3 = UnpackArray<string[]>           // string

// 获取函数返回类型(手写 ReturnType)
type MyReturnType<T extends (...args: any) => any> =
  T extends (...args: any) => infer R ? R : never

// 获取 Promise 最终值(递归解包)
type DeepAwaited<T> =
  T extends Promise<infer U> ? DeepAwaited<U> : T

type R = DeepAwaited<Promise<Promise<string>>>  // string

// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never
type R4 = ToArray<string | number>  // string[] | number[]
// 注意:T extends any 会对联合类型的每个成员分别应用

// 阻止分布:用元组包裹
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type R5 = ToArrayNonDist<string | number>  // (string | number)[]

映射类型

ts
// 基本映射
type Readonly<T> = {
  readonly [K in keyof T]: T[K]
}

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

// 修改符:+ 添加,- 移除
type Mutable<T> = {
  -readonly [K in keyof T]: T[K]  // 移除 readonly
}

type Required<T> = {
  [K in keyof T]-?: T[K]  // 移除 ?(可选)
}

// 键重映射(as 子句,TS 4.1)
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 }

// 过滤属性
type FilterByType<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K]
}

type StringProps = FilterByType<User, string>  // { name: string }

实战:类型安全的工具函数

ts
// 深度只读
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}

// 深度可选
type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}

// 路径类型(访问嵌套属性)
type Paths<T, Prefix extends string = ''> = {
  [K in keyof T & string]:
    T[K] extends object
      ? Paths<T[K], `${Prefix}${K}.`> | `${Prefix}${K}`
      : `${Prefix}${K}`
}[keyof T & string]

interface Config {
  server: { host: string; port: number }
  db: { url: string }
}
type ConfigPaths = Paths<Config>
// 'server' | 'server.host' | 'server.port' | 'db' | 'db.url'

// 类型安全的 get 函数
type Get<T, Path extends string> =
  Path extends `${infer Key}.${infer Rest}`
    ? Key extends keyof T
      ? Get<T[Key], Rest>
      : never
    : Path extends keyof T
      ? T[Path]
      : never

function get<T, P extends Paths<T>>(obj: T, path: P): Get<T, P> {
  return path.split('.').reduce((acc: any, key) => acc[key], obj)
}

const config: Config = { server: { host: 'localhost', port: 3000 }, db: { url: 'postgres://...' } }
get(config, 'server.port')  // 类型是 number ✓
// get(config, 'server.xxx')  // ❌ 编译错误

泛型与 React

tsx
// 泛型组件
interface ListProps<T> {
  items: T[]
  renderItem: (item: T, index: number) => React.ReactNode
  keyExtractor: (item: T) => string
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, i) => (
        <li key={keyExtractor(item)}>{renderItem(item, i)}</li>
      ))}
    </ul>
  )
}

// 使用:完全类型安全
<List
  items={users}
  keyExtractor={u => String(u.id)}
  renderItem={u => <span>{u.name}</span>}
/>

// 泛型 Hook
function useLocalStorage<T>(key: string, initialValue: T) {
  const [value, setValue] = useState<T>(() => {
    try {
      const item = localStorage.getItem(key)
      return item ? JSON.parse(item) : initialValue
    } catch {
      return initialValue
    }
  })

  const setStoredValue = (newValue: T | ((prev: T) => T)) => {
    const valueToStore = newValue instanceof Function ? newValue(value) : newValue
    setValue(valueToStore)
    localStorage.setItem(key, JSON.stringify(valueToStore))
  }

  return [value, setStoredValue] as const
}

// 使用
const [theme, setTheme] = useLocalStorage('theme', 'light')
// theme 类型是 string,setTheme 类型是 (v: string | ((p: string) => string)) => void

总结

  • extends 约束泛型范围,keyof 约束为对象的键
  • infer 在条件类型中提取类型,是高级类型体操的核心
  • 映射类型 + as 子句可以过滤和重命名属性
  • 泛型组件和泛型 Hook 是 React + TS 的最佳实践
  • 递归泛型处理嵌套结构(DeepReadonly、DeepPartial)

系统学习 Web 前端生态,深入底层架构