要理解在TypeScript中使用
keyof typeof
,首先需要了解什么是
字面类型和
字面类型的联合。因此,我将首先解释这些概念,然后详细解释
keyof
和
typeof
。之后,我将回到
enum
来回答问题中所需的内容。虽然这是一个较长的答案,但示例易于理解。
字面类型
TypeScript中的字面类型是string
、number
或boolean
的更具体类型。例如,"Hello World"
是一个string
,但string
并不是"Hello World"
。因此,"Hello World"
是一种更具体的类型,即字面类型。
可以声明字面类型如下:
type Greeting = "Hello"
这意味着类型为
Greeting
的对象只能有一个
string
值
"Hello"
,不能有其他任何
string
值或任何其他类型的值,如下面的代码所示:
let greeting: Greeting
greeting = "Hello" // OK
greeting = "Hi" // Error: Type '"Hi"' is not assignable to type '"Hello"'
字面类型本身并不实用,但是当与联合类型、类型别名和类型保护结合使用时,它们变得强大。
以下是字面类型联合的示例:
type Greeting = "Hello" | "Hi" | "Welcome"
现在类型为
Greeting
的对象可以有值
"Hello"
、
"Hi"
或
"Welcome"
。
let greeting: Greeting
greeting = "Hello" // OK
greeting = "Hi" // OK
greeting = "Welcome" // OK
greeting = "GoodEvening" // Error: Type '"GoodEvening"' is not assignable to type 'Greeting'
只有keyof
keyof
用于某个类型 T
,它会给你一个新类型,这个新类型是字面量类型的联合,这些字面量类型是 T
的属性名。结果类型是字符串的子类型。
例如,考虑下面的 interface
:
interface Person {
name: string
age: number
location: string
}
在类型Person
上使用keyof
运算符将会得到一个新的类型,如下面的代码所示:
type SomeNewType = keyof Person
SomeNewType
是一个由 Person
类型的属性构成的字面类型联合("name" | "age" | "location"
)。
现在,您可以创建 SomeNewType
类型的对象:
let newTypeObject: SomeNewType
newTypeObject = "name" // OK
newTypeObject = "age" // OK
newTypeObject = "location" // OK
newTypeObject = "anyOtherValue" // Error...
在对象上同时使用keyof typeof
如您所知,typeof
运算符可以给出一个对象的类型。
在上面的Person
接口示例中,我们已经知道了类型,因此只需在类型Person
上使用keyof
运算符。
但是当我们不知道对象的类型或者我们只有一个值而不是该值的类型时,该怎么办呢?例如下面的情况:
const bmw = { name: "BMW", power: "1000hp" }
这是我们一起使用 keyof typeof
的地方。
typeof bmw
给出类型: { name: string, power: string }
然后 keyof
运算符给出如下代码中所示的字面类型联合:
type CarLiteralType = keyof typeof bmw
let carPropertyLiteral: CarLiteralType
carPropertyLiteral = "name"
carPropertyLiteral = "power"
carPropertyLiteral = "anyOther"
keyof typeof
在 enum
上的使用
在 TypeScript 中,枚举被用作编译时的类型,以实现常量的类型安全性,但它们在运行时被视为对象。这是因为,一旦 TypeScript 代码被编译为 JavaScript,它们就会被转换为普通对象。因此,上面的对象解释也适用于此处。问题中 OP 给出的示例是:
enum ColorsEnum {
white = '#ffffff',
black = '#000000',
}
这里的ColorsEnum
是一个运行时存在的对象,而不是一个类型。因此,我们需要像以下代码一样同时调用keyof typeof
操作符:
type Colors = keyof typeof ColorsEnum
let colorLiteral: Colors
colorLiteral = "white" // OK
colorLiteral = "black" // OK
colorLiteral = "red" // Error...