首页 > 解决方案 > 在 for in 循环中丢失枚举类型信息

问题描述

在下面的示例中,键的myColors类型为Color。但是,我在遍历myColor对象时无法保留该信息。

enum Color {
  Red = 'RED',
  Green = 'GREEN',
}

type MyColors = {
  [C in Color]?: string;
}

const myColors: MyColors = {
  [Color.Red]: '#8B0000',
}

for (const [name, hex] of Object.entries(myColors)) {
  // name is of type string here instead of Color
}

使用常规for in循环,或者Object.keys(myColors)也将枚举键转换为字符串。

循环遍历对象的属性时,有什么方法可以保留键的类型?如果不是,我怎么能断言name在我的循环中是 type Color

标签: typescriptenums

解决方案


TypeScript 的 for 定义Object.entries被重载,但两个重载都明确地使用字符串作为键类型。来自lib/lib.es2017.object.d.ts

/**
 * Returns an array of key/values of the enumerable properties of an object
 * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
 */
entries<T>(o: { [s: string]: T } | ArrayLike<T>): [string, T][];

/**
 * Returns an array of key/values of the enumerable properties of an object
 * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
 */
entries(o: {}): [string, any][];

我确信这是有充分理由的。:-) 但是您可以定义自己的entries不使用string

// Our `entries` function
function entries<T>(o: T): [keyof T, T[keyof T]][] {
    return Object.entries(o) as unknown as [keyof T, T[keyof T]][];
}

然后它工作:

for (const [name, hex] of entries(myColors)) {
  // type of `name` is `Color` now
}

更进一步,我发现如果我添加以下声明:

interface ObjectConstructor {
    /**
     * Returns an array of key/values of the enumerable properties of an object
     * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
     */
    entries<T>(o: T): [keyof T, T[keyof T]][];
}

这样,您的原始代码就可以按预期工作:

for (const [name, hex] of Object.entries(myColors)) {
  // type of `name` is `Color` now
}

(我用真正的编译器仔细检查了这一点,而不仅仅是操场,通过const n: Color = name;在循环体中。TypeScript 抱怨它没有声明,但有声明它很高兴。)

但是,TypeScript 问题列表中的一些问题让我认为定义可能会对您传递的其他类型的事情造成麻烦Object.entries,特别是这个这个评论。因此,您可能希望拥有单独的功能(这很可能会使 JIT 消失)并在相关的地方使用它。


推荐阅读