泛型约束
有一个这样的代码
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "Alice", age: 30 };
const name = getValue(user, "name"); // string
const age = getValue(user, "age"); // number
为什么 K 可以 extends keyof T ?keyof T 是接口吗?
在 TypeScript 中,K extends keyof T
是一个泛型约束,它表示类型参数 K
必须是 T
类型的所有可能键名的联合类型的子类型。我来详细解释这个概念:
1. keyof T
是什么?
keyof T
是一个索引类型查询操作符,它会返回T
类型的所有公共属性名的联合类型- 如果
T
是一个接口或对象类型,keyof T
就是它的所有键名的字符串字面量联合类型
示例:
interface Person {
name: string;
age: number;
}
type PersonKeys = keyof Person;
// 等同于: "name" | "age"
2. K extends keyof T
的含义
- 这里的
extends
表示泛型约束,不是类继承 - 它限制
K
只能是keyof T
的子集(即只能是T
的合法属性名) - 这样就能保证
obj[key]
的访问是类型安全的
3. 为什么可以这样写?
- TypeScript 的类型系统允许这种约束
keyof T
本身就是一个类型(联合类型)extends
在泛型中表示"必须是…的子类型"
4. 实际应用示例
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "Alice", age: 30 };
// 正确 - "name" 是 Person 的键
const name = getValue(person, "name"); // string 类型
// 正确 - "age" 是 Person 的键
const age = getValue(person, "age"); // number 类型
// 错误 - "gender" 不是 Person 的键
const gender = getValue(person, "gender"); // 编译时报错
5. 与接口的关系
keyof T
不是接口,而是一个类型操作符- 当
T
是接口时,keyof T
会生成该接口所有属性的联合类型 - 但
T
也可以是其他类型,如:type TupleKeys = keyof [string, number]; // "0" | "1" | "length" | ...
这种设计提供了强大的类型安全性,确保:
- 只能访问对象实际存在的属性
- 返回值类型与属性类型精确匹配
- 在编译时就能捕获拼写错误或无效的属性访问