TypeScript 中的“keyof typeof”是啥意思?
Posted
技术标签:
【中文标题】TypeScript 中的“keyof typeof”是啥意思?【英文标题】:What does "keyof typeof" mean in TypeScript?TypeScript 中的“keyof typeof”是什么意思? 【发布时间】:2019-08-18 00:42:49 【问题描述】:向我解释一下keyof typeof
在 TypeScript 中的含义
例子:
enum ColorsEnum
white = '#ffffff',
black = '#000000',
type Colors = keyof typeof ColorsEnum;
最后一行相当于:
type Colors = "white" | "black"
但是它是如何工作的呢?
我希望 typeof ColorsEnum
返回类似 "Object"
的内容,然后 keyof "Object"
不会做任何有趣的事情。但我显然错了。
【问题讨论】:
【参考方案1】:要了解 TypeScript 中 keyof typeof
的用法,首先需要了解什么是文字类型和文字类型的联合。所以,我先解释一下这些概念,然后再详细解释keyof
和typeof
。之后,我会回到enum
回答问题中的问题。这是一个很长的答案,但例子很容易理解。
文字类型
TypeScript 中的文字类型是更具体的 string
、number
或 boolean
类型。例如,"Hello World"
是 string
,但 string
不是 "Hello World"
。 "Hello World"
是类型 string
的更具体的类型,所以它是一个字面量类型。
文字类型可以声明如下:
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
of some type 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" // OK
carPropertyLiteral = "power" // OK
carPropertyLiteral = "anyOther" // Error...
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...
就是这样!希望对您有所帮助。
【讨论】:
这是一个典型的 Stack Overflow 答案,应该用来向新用户解释如何回答问题。非常好,这应该是公认的答案。很棒。 我需要每月阅读这个答案。 这绝对是一个教科书式的回答,说明什么是好的回复。【参考方案2】:keyof
接受一个对象类型并返回一个接受任何对象键的类型。
type Point = x: number; y: number ;
type P = keyof Point; // type '"x" || "y"'
const coordinate: P = 'z' // Type '"z"' is not assignable to type '"x" | "y"'.
typeof 与 TypeScript 类型
typeof
在 javascript 对象上调用时的行为与在 typescript 类型上调用时的行为不同。
"undefined", "object", "boolean", "number", "bigint", "string", "symbol", "function"
之一
TypeScript's typeof 在类型值上调用,但在类型表达式中也可以在 javascript 值上调用。它还可以推断 javascript 对象的类型,返回更详细的对象类型。
type Language = 'EN' | 'ES';
const userLanguage: Language = 'EN';
const preferences = language: userLanguage, theme: 'light' ;
console.log(typeof preferences); // "object"
type Preferences = typeof preferences; // type 'language: 'EN''; theme: string; '
因为第二个 typeof preferences
在类型表达式中,它实际上是 TypeScript 自己的 typeof
被调用,而不是 javascript。
keyof 类型
因为keyof
是一个TypeScript 概念,我们将调用TypeScript 的typeof
版本。
keyof typeof
将推断 javascript 对象的类型并返回其键的联合类型。因为它可以推断出键的确切值,所以它可以返回它们的 literal types 的联合,而不仅仅是返回“字符串”。
type PreferenceKeys = keyof typeof preferences; // type '"language" | "theme"'
【讨论】:
【参考方案3】:enum
创建一个实例化的object
。使用typeof
,我们得到了这个enum
的自动生成类型。
现在我们可以使用keyof
获取所有索引,以确保Colors
只能包含其中一个。
【讨论】:
【参考方案4】:关于 TypeScript 的常见误解
TypeScript 通常被描述为 JavaScript 运行时之上的类型层。好像类型和值存在于不同的平面上。然而,在 TypeScript 中,有些东西同时是类型 和 值。
这适用于:
类, 枚举, 命名空间。什么时候可以使用keyof
?
keyof
关键字仅适用于类型级别。您不能将其应用于 JavaScript 值。
什么时候需要keyof typeof
?
当您同时处理类型和值(如类或枚举),但您特别感兴趣的是该值的类型时。
最简单的例子:
const foo = bar: 42 ; // foo is a value
type Foo = typeof foo; // Foo is the type of foo
type KeyOfFoo = keyof Foo; // "keyof Foo" is the same as "keyof typeof foo", which is "bar"
一般来说,当你看到这个时:
type A = keyof typeof B;
typeof B
部分告诉 TypeScript 查看 B 的 type。您可以将其视为将 B 转换为它的类型。有点像将二维对象投射到一维空间。
由于typeof B
是一个类型,而不是一个值,我们现在可以在其上使用keyof
。
示例
类是类型和值。你可以打电话给他们,也可以在他们身上使用keyof
。
declare class Foo
static staticProperty: string;
dynamicProperty: string;
type Constructor = typeof Foo;
type Instance = Foo;
type A = keyof Constructor; // "prototype" | "staticProperty"
type B = keyof Instance; // "dynamicProperty"
通过将typeof
与keyof
一起使用,我们可以在针对instance 类型和constructor 类型使用keyof
之间切换。
【讨论】:
【参考方案5】:为了查找任何值的类型,我们使用 typeof 操作。例如
const user =
getPersonalInfo(),
getLocation()
这里的 user 是一个值,所以 typeof 操作符就派上用场了
type userType = typeof user
这里 userType 给出了用户是一个对象的类型信息,它有两个属性 getPersonalInfo 和 getLocation 并且都是返回 void 的函数
现在如果你想找到用户的密钥,你可以使用 keyof
type userKeys = keyof userType
上面写着 userKeys= 'getPersonalInfo'| 'getLocation'
请注意,如果您尝试获取用户的密钥,例如
type userKeys = keyof user
,您将收到错误 'user' 引用了一个值,但在这里被用作类型。你是说'typeof user'吗?
【讨论】:
以上是关于TypeScript 中的“keyof typeof”是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章
Typescript 入门手册之函数类型在 TypeScript 中的应用
Typescript 入门手册之函数类型在 TypeScript 中的应用
Typescript入门手册之引用类型在TypeScript中的应用