"keyof typeof" 在 TypeScript 中是什么意思?

What does "keyof typeof" mean in TypeScript?

给我解释一下 keyof typeof 在 TypeScript 中的意思

示例:

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

type Colors = keyof typeof ColorsEnum;

最后一行相当于:

type Colors = "white" | "black"

但是它是如何工作的?

我希望 typeof ColorsEnum 到 return 类似 "Object" 然后 keyof "Object" 不会做任何有趣的事情。但我显然错了。

一个 enum 创建一个实例化的 object。使用 typeof 我们得到这个 enum.

的自动生成类型

现在我们可以获取所有带有 keyof 的索引,以确保 Colors 只能包含其中一个。

要了解 keyof typeof 在 TypeScript 中的用法,首先您需要了解什么是 文字类型 文字类型的联合 .所以,我会先解释这些概念,然后再分别详细解释keyoftypeof。之后,我会回到 enum 来回答问题中的问题。答案很长,但示例很容易理解。


文字类型

TypeScript 中的文字类型是更具体的 stringnumberboolean 类型。例如,"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 某种类型 T 给你一个新类型,它是 文字类型的联合 并且这些文字类型是属性的名称T。结果类型是字符串的子类型。

例如,考虑以下 interface

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

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

type SomeNewType = keyof Person

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 接口示例中,我们已经知道类型,所以我们只需要在类型 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 typeofenum

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

就是这样!希望对您有所帮助。

关于 TypeScript 的常见误解

TypeScript 通常被描述为 JavaScript 运行时之上的类型层。好像类型和值生活在不同的平面上。但是,在 TypeScript 中,有些东西同时是类型 值。

这适用于:

  • classes,
  • 枚举,
  • 名字spaces.

什么时候可以使用keyof

keyof 关键字仅适用于类型级别。您不能将其应用于 JavaScript 值。

什么时候需要keyof typeof?

当您同时处理既是类型又是值的事物(例如 class 或枚举)时,但您对该值的类型特别感兴趣.

最简单的例子:

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 转换为其类型。有点像将二维对象转换为一维对象 space.

由于 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"

通过将 typeofkeyof 一起使用,我们可以在针对 实例 类型和 [=51= 使用 keyof 之间切换]构造函数类型。

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

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

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

type userType = typeof user

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

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

type userKeys = keyof userType

这表示 userKeys= 'getPersonalInfo'| 'getLocation'

Beware if you try to get user's key like type userKeys = keyof user you will get an error 'user' refers to a value, but is being used as a type here. Did you mean 'typeof user'?

keyof 采用对象类型,return 是接受任何对象键的类型。

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 在运行时调用 javascript 值时使用 javascript's typeof 并且 return 是 "undefined", "object", "boolean", "number", "bigint", "string", "symbol", "function"
  • 之一
  • TypeScript's typeof 在类型值上调用,但在类型表达式中也可以在 javascript 值上调用。它还可以推断 javascript 对象的类型,returning 更详细的对象类型。
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 typeof

因为 keyof 是一个 TypeScript 概念,我们将调用 TypeScript 的版本 typeof

keyof typeof 将推断出 javascript 对象的类型,而 return 是其键的联合类型。因为它可以推断出键的确切值,所以它可以 return 它们 literal types 的并集,而不仅仅是 returning "string".

type PreferenceKeys = keyof typeof preferences; // type '"language" | "theme"'