泛型约束(<T extends ...>)到底在“约束”什么

8 阅读1分钟

它约束的是:传进来的泛型 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)