175

向我解释keyof typeofTypeScript 中的含义

例子:

enum ColorsEnum {
    white = '#ffffff',
    black = '#000000',
}

type Colors = keyof typeof ColorsEnum;

最后一行相当于:

type Colors = "white" | "black"

但它是如何工作的?

我希望typeof ColorsEnum返回类似的东西"Object",然后keyof "Object"不做任何有趣的事情。但我显然错了。

4

5 回答 5

369

要了解keyof typeofTypeScript 中的用法,首先需要了解什么是字面量类型和字面量类型的联合。因此,我将首先解释这些概念,然后再keyof详细解释typeof。之后,我会回来enum回答问题中的问题。这是一个很长的答案,但例子很容易理解。


文字类型

TypeScript 中的文字类型是更具体的string,numberboolean. 例如,"Hello World"is a string,但 a stringis not "Hello World""Hello World"是 type 的一种更具体的类型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只要

keyofof some typeT给你一个新类型,它是文字类型的联合,这些文字类型是T. 结果类型是字符串的子类型。

例如,考虑以下内容interface

interface Person {
    name: string
    age: number
    location: string
}

在类型上使用keyof运算符Person将​​为您提供一个新类型,如以下代码所示:

type SomeNewType = keyof Person

这是由 type 的属性组成SomeNewType的文字类型 () 的联合。"name" | "age" | "location"Person

现在您可以创建类型的对象SomeNewType

let newTypeObject: SomeNewType
newTypeObject = "name"           // OK
newTypeObject = "age"            // OK
newTypeObject = "location"       // OK
newTypeObject = "anyOtherValue"  // Error...

keyof typeof一起在一个物体上

您可能已经知道,typeof运算符为您提供对象的类型。在上面的Person接口示例中,我们已经知道类型,所以我们只需要keyof在 type 上使用运算符Person

但是当我们不知道对象的类型或者我们只有一个值而不是像下面这样的值的类型时该怎么办?

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...

而已!希望有帮助。

于 2020-07-06T21:05:30.897 回答
48

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 类型上调用时的行为不同。

  • TypeScript 在运行时对 javascript 值调用时使用javascript 的 typeof并返回其中之一"undefined", "object", "boolean", "number", "bigint", "string", "symbol", "function"
  • TypeScript 的 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是一个 TypeScript 概念,我们将调用 TypeScript 的typeof.

keyof typeof将推断 javascript 对象的类型并返回一个类型,该类型是其键的并集。因为它可以推断出键的确切值,所以它可以返回其文字类型的联合,而不仅仅是返回“字符串”。

type PreferenceKeys = keyof typeof preferences; // type '"language" | "theme"'
于 2021-01-29T16:18:39.663 回答
31

An enum creates an instantiated object. With typeof we get the auto generated type of this enum.

Now we can get all indices with keyof to make sure Colors can only contain one of them.

于 2019-03-27T12:44:15.973 回答
14

关于 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 的类型。您可以将其视为将 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与 with 一起使用,我们可以在使用实例类型和构造函数keyof类型之间切换。keyof

于 2020-07-14T21:25:53.370 回答
2

为了找到任何值的类型,我们使用 typeof 操作。例如

const user = {
   getPersonalInfo(){},
   getLocation(){}
}

这里 user 是一个值,所以这里 typeof 运算符会派上用场

type userType = typeof user

这里 userType 给出了用户是一个对象的类型信息,它有两个属性 getPersonalInfo 和 getLocation 并且都是函数 return void

现在,如果您想查找用户的密钥,可以使用 keyof

type userKeys = keyof userType

其中说 userKeys= 'getPersonalInfo'| '获取位置'

请注意,如果您尝试获取用户的密钥,就像type userKeys = keyof user您会收到错误“用户”引用一个值,但在此处用作类型一样。您的意思是“typeof 用户”吗?

于 2020-09-14T15:35:22.480 回答