基础概念
keyof操作符允许我们从一个对象类型中提取所有键名(属性名)组成一个联合类型。这一功能对于编写类型安全的访问器函数、映射类型以及泛型约束特别有用。
interface Person {
name: string;
age: number;
}
通过keyof操作符,我们可以获取Person接口的所有键名组成的类型
type PersonKey = keyof Person; // 结果为:'name' | 'age'
PersonKey类型表示的是name和age的联合类型,这为后续的类型操作提供了基础。
与映射类型结合使用
keyof真正厉害在于它与映射类型的合作。映射类型允许我们基于一个现有的类型创建新的类型,而keyof提供了映射的“索引”。例如,我们可以通过映射类型来创建一个具有可选属性的相同结构类型:
type PersonWithOptionalKeys = {
[K in keyof Person]?: Person[key];
}
// 结果为:{ name?: string; age?: number; }
这样,PersonWithOptionalKeys类型就允许name和age属性为可选。
泛型中的应用
在泛型上下文中,keyof可以作为类型参数约束,使泛型变得更加灵活且类型安全。考虑一个通用的属性获取函数:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K]{
return obj[key];
}
const person: Person = { name: 'PS', age: 18 }
console.log(getProperty(person, 'name')); // "PS"
// 下面这行会报错,因为 'sex' 不是 Person 的一个属性
// console.log(getPersonField(person, 'sex'));
这个函数接受任何对象T和其键名K,并安全地返回对应的属性值。类型系统会确保key参数只能是T类型的实际键名之一。
提取特定类型的属性
有时候我们需要从一个类型中提取特定类型的属性。例如,我们想要从一个借口中提取所有方法。
interface Person {
time: number;
eat: (msg: string) => Primise(string);
wear: (msg: string) => number;
}
type Methods<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
type PersonMethods = Methods<Person>; // "eat" | "wear"
类型安全的switch语句
在处理动态键值时,确保switch语句的类型安全至关重要,通过keyof和类型断言,我们可以实现这一点:
function handleProperty(person: Person, key: keyof Person){
switch(key){
case 'name':
console.log(person.name.toUpperCase());
break;
case 'age':
console.log(`Age: ${person.age}`);
break;
// 无需 default 分支,因为 TypeScript 确保 key 只能是 'name' 或 'age'
}
}
这段代码中,由于key的类型限制,编译器会强制我们处理所有可能的键值,从而避免了逻辑上的遗漏。
我是菜逼,大佬勿喷。