以下内容是官方文档相关翻译:
对一个对象类型使用 keyof 操作符,会返回该对象属性名组成的一个字符串或者数字字面量的联合。这个例子中的类型P 就等同于"x" | "y":
type Point = { x: number; y: number };
type P = keyof Point;
// type P = keyof Point
但如果这个类型有一个 string 或者 number 类型的索引签名,keyof 则会直接返回这些类型:
type Arrayish = { [n: number]: unknown };
type A = keyof Arrayish;
// type A = number
type Mapish = { [k: string]: boolean };
type M = keyof Mapish;
// type M = string | number
注意在这个例子中,M 是 string | number,这是因为 JavaScript 对象的属性名会被强制转为一个字符串,所以 obj[0] 和 obj["0"] 是一样的。
到这里,官方介绍结束。
接下来,我们想要理解keyof的原理。首先需要理解字面量类型(literal types)和联合字面量类型(union of literal types)。
字面量类型 & 联合字面量类型
在TS中,字面量类型是更具体的string,number或boolean类型。比如"Hello World"是一个string,但是string类型不是"Hello World"。
type Animal = "dog"
这样意味着Animal类型只有一个人字符串的值"dog",没有其他string类型的值。
type Animal = "dog" | "cat" | "elephant"
现在,Animal类型对象的值可以是"dog","cat","elephant"。
使用keyof
值得注意的点,keyof可以返回一个数字字面量的联合类型。比如有一个类型T,keyof T将会得到一个新的联合字面量类型。组成它的字面量类型是T的属性名称,最后生产的类型是String的子类型。
举个例子:
interface Person {
name: string
age: number
gender: string
}
type NewType = keyof Person
这里, "keyof Person"会得到一个新类型, NewType是联合字面量类型("name" | "age" | gender)。
let newObj: NewType;
newObj = "name"; // ok
newObj = "age"; // ok
newObj = "gender"; // ok
newObj = "height"; // Type '"height"' is not assignable to type 'keyof Person'.
keyof typeof同时使用(扩展内容)
单独使用keyof,是因为上面的例子中,我们已经知道Person的类型。只需要对Person使用keyof即可。
const example = { name: "leo", age: 20, gender: "male"}
这时候,就需要keyof typeof一起使用。
typeof example 会得到对象的类型 { name: string, age: number, gender: string }
type LiteralType = keyof typeof example;
let obj: LiteralType;
obj = "name"; // ok
obj = "age"; // ok
obj = "height"; // Type '"height"' is not assignable to type '"name" | "age" | "gender"'.
在TS中,enum下编译时被用作类型,来实现常量的类型安全。但是枚举在运行时被视为对象。这是因为,当TS代码被编译为JS的时候,他们会被转换成普通的对象。我们来看下面的枚举:
enum Colors {
white = '#FFFFFF',
red = '#FF0000',
}
上述代码的Colors在运行时是作为一个对象存在,不是一个类型,因此,需要使用"keyof typeof"。
type ColorsKey = keyof typeof Colors;
let key: ColorKeys;
key = 'white'; // ok
key = 'red'; // ok
key = 'blue'; // Type '"blue"' is not assignable to type '"white" | "black" | "red"'.