FumadocsZDecode
Typescript

泛型详解

泛型让类型成为参数,复用同一份逻辑处理多种类型。它是贯穿 TypeScript 类型系统的载体——映射类型、条件类型、工具类型都建立在泛型之上。


一、基础泛型

<T> 声明一个类型参数,在使用时才确定具体类型。

function identity<T>(value: T): T {
  return value
}

identity<string>('hello') // 显式指定 T = string
identity(42) // 自动推导 T = number

泛型可用于接口、类,并支持约束和默认值:

// 泛型接口
interface Box<T> {
  value: T
}

// 泛型类
class Container<T> {
  constructor(public content: T) {}
}

// 泛型约束:保证 T 一定有 length 属性
function getLength<T extends { length: number }>(arg: T): number {
  return arg.length
}

// 默认泛型参数
interface Options<T = string> {
  data: T
}

二、类型推导与 infer

TS 会自动从值或上下文推断类型,无需显式标注:

// eslint-disable-next-line prefer-const
let x = 3 // number
const y = 'hi' // 'hi'(字面量类型)
// eslint-disable-next-line prefer-const
let arr = [1, 2, 3] // number[]

// 上下文推导:参数 n 自动推为 number
;[1, 2, 3].map((n) => n * 2)

infer 用于在条件类型中提取某个位置的类型:

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

// 提取数组元素类型
type Unpack<T> = T extends Array<infer U> ? U : T
type B = Unpack<number[]> // number

三、条件类型

T extends U ? X : Y 是类型层面的三元运算。

type IsString<T> = T extends string ? true : false
type C1 = IsString<'a'> // true
type C2 = IsString<1> // false

分布式条件类型:当 T 是联合类型时,会逐个分发处理:

type ToArray<T> = T extends any ? T[] : never
type D = ToArray<string | number> // string[] | number[]

// 用 [T] 元组包裹可阻止分发
type NoDistribute<T> = [T] extends [any] ? T[] : never
type D2 = NoDistribute<string | number> // (string | number)[]

结合 infer 提取元素类型:

type ElementType<T> = T extends (infer U)[] ? U : never
type E = ElementType<boolean[]> // boolean

四、映射类型

基于已有类型的键,用 [K in keyof T] 批量生成新类型。

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

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

修饰符增删- 移除,+ 添加(默认)。

// 去掉 readonly
type Mutable<T> = {
  -readonly [K in keyof T]: T[K]
}

// 去掉可选,变必填
type MyRequired<T> = {
  [K in keyof T]-?: T[K]
}

key remapping:用 as 重命名或过滤键。

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

// 过滤:键值为 never 的键被剔除
type RemoveNever<T> = {
  [K in keyof T as T[K] extends never ? never : K]: T[K]
}

五、内置工具类型

工具类型是映射类型和条件类型的封装产物:

工具类型作用实现核心
Partial<T>所有属性变可选映射 + ?
Required<T>所有属性变必选映射 + -?
Readonly<T>所有属性只读映射 + readonly
Pick<T, K>挑选部分键[P in K]: T[P]
Omit<T, K>排除部分键Pick<T, Exclude<keyof T, K>>
Record<K, V>构造键值映射{ [P in K]: V }
Exclude<T, U>从联合剔除条件类型分发
Extract<T, U>从联合提取条件类型分发
NonNullable<T>去除 null/undefinedExclude<T, null | undefined>
ReturnType<T>取函数返回值类型infer R
Parameters<T>取函数参数元组infer P
Awaited<T>解包 Promise递归 infer

部分手写实现,理解底层原理:

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

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

type MyRecord<K extends keyof any, V> = {
  [P in K]: V
}

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

关系链:泛型是贯穿全部的载体,类型推导(含 infer)负责自动填充类型参数;映射类型和条件类型是底层机制,内置工具类型则是它们的封装产物。

On this page