它约束的是:传进来的泛型 T 必须满足某种结构/范围,这样你在函数内部就能安全地使用某些属性/方法。、
理解:T extends X = “T 至少要长得像 X(至少拥有 X 的那些成员)”。
例子 1:为什么需要约束(否则 TS 不让你用 .length)
function len<T>(x: T) {
return x.length // ❌ 报错:T 不一定有 length
}
加上约束:
function len<T extends { length: number }>(x: T) {
return x.length // ✅ 安全:因为 T 保证有 length:number
}
len('abc') // ✅ string 有 length
len([1, 2]) // ✅ array 有 length
len(123) // ❌ number 没有 length,调用直接被 TS 拦住
例子 2:约束到“某些 key”(K extends keyof T)
这是项目里最常用的一类约束:让你只能传对象存在的 key。
function getProp<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
const user = { id: 1, name: 'Tom' }
getProp(user, 'id') // ✅ 返回 number
getProp(user, 'name') // ✅ 返回 string
getProp(user, 'age') // ❌ 报错:'age' 不在 keyof user 里
- T 是对象类型
- K 被约束为 T 的键集合(keyof T)
- 所以你不可能传错字段名,返回值类型也能精确推导出来
例子 3:约束成“联合类型范围”
type Method = 'GET' | 'POST'
function request<TMethod extends Method>(m: TMethod) {
return m
}
request('GET') // ✅
request('DELETE') // ❌
3句话记住泛型约束:
- 不加约束:T 什么都可能是,函数里就不能随便访问属性
- 加 extends 约束:T 至少满足某个结构,你就能安全使用那部分能力
- 常见套路:T extends { ... }(约束结构)和 K extends keyof T(约束 key)