泛型深度解析
泛型是 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)