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/undefined | Exclude<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)负责自动填充类型参数;映射类型和条件类型是底层机制,内置工具类型则是它们的封装产物。