如果存在TypeScript枚举值,还有没有更优雅的方法?

时间:2019-02-14 13:23:07

标签: typescript enums

如果我有一个允许值的枚举,例如:

enum PackagingUnits {
  Be = 'becher',
  Bg = 'bogen',
  Bi = 'bidon'
}

我希望函数通过键传递一个字符串以获取枚举值,因此getPackagingUnitName('Be')将返回'becher'

这个天真的版本是:

function getPackagingUnitName(key: string): PackagingUnits | null {
  if (PackagingUnits[key]) {
    return PackagingUnits[key]
  }
  return null
}

但是,TypeScript(版本3.3.3 atm)不喜欢这样,并且抱怨Element implicitly has an 'any' type because index expression is not of type 'number'.的每个实例都PackagingUnits[key]

我尝试了if条件的一些变体(例如Object.keys(PackagingUnits).includes(key),它使if条件无错误,但是对于return语句return PackagingUnits[key]仍然给出相同的错误。

return PackagingUnits[key as PackagingUnits]也不起作用。

我唯一能想到的有效语法是:

function getPackagingUnitName(key: string): PackagingUnits | null {
  const puIndex = Object.keys(PackagingUnits).indexOf(key)
  if (puIndex !== -1) {
    return Object.values(PackagingUnits)[puIndex]
  }
  return null
}

这真的是最好的方法吗?

N.B .:是的,如果PackagingUnits不是枚举,则可以更轻松地完成此操作,但对于其他用途则需要这样做。

2 个答案:

答案 0 :(得分:3)

TypeScript不会将访问未知属性视为缩小控制流以断言该属性存在的一种方式。仅当编译器知道该键处有一个属性(或在使用可选属性的情况下)时,才可以访问对象类型的属性。因此,一旦您执行PackagingUnits[key],其中key是任意string时,编译器就会抱怨PackagingUnits没有string索引。

最简单的解决方法是使用type assertion并继续:

function getPackagingUnitName(key: string): PackagingUnits | null {      
  if ((PackagingUnits as any)[key]) { // assert your way out
    return PackagingUnits[key as keyof typeof PackagingUnits]; // assert your way out
  }
  return null;
}

这有效,但不能保证编译器的类型安全。您可能不在乎,因为问题出在其他开发人员不会使用的功能的实现中。如果是这样,那就太好了。


如果希望编译器保证所执行的操作是类型安全的,则需要通过仔细的步骤引导其进行分析。首先,您希望编译器不要将PackagingUnits对象当作具有三个已知值为PackagingUnits类型的键的对象来对待,而是将其视为值为 every < / em>字符串键,其值为PackagingUnits | undefined。 (由于TypeScript并不总是optional index signatures,因此距离distinguish missing properties from undefined-values properties很近)。所以你想要这样:

const PackagingUnitsIndexSignature: {[k: string]: PackagingUnits | undefined} =
  PackagingUnits; // error!

这应该是安全的(类型typeof PackagingUnitstypeof PackagingUnitsIndexSignature的严格subtype),但是编译器没有意识到。 enum的{​​{1}}性质似乎使它感到困惑。因此,我们可以分两步进行扩展...首先是将其键和值与枚举的键和值相匹配的普通对象类型:

PackagingUnits

现在我们终于有了可以用const PackagingUnitsPlainObject: Record<keyof typeof PackagingUnits, PackagingUnits> = PackagingUnits; // okay! const PackagingUnitsIndexSignature: { [k: string]: PackagingUnits | undefined } = PackagingUnitsPlainObject; // okay! 进行索引的东西了,没有错误:

string

U,发生了什么事?由于我们刚刚检查了function getPackagingUnitName(key: string): PackagingUnits | null { if (PackagingUnitsIndexSignature[key]) { // okay! return PackagingUnitsIndexSignature[key]; // error!!!! } else { return null; } } 处某个属性的存在,是否应该控制流量收窄工作?不幸的是,bracket-type indexed access doesn't always narrow values the way we'd like。让我们以key返回PackagingUnitsIndexSignature[key]为前提,而不是强制执行此操作。如果是这样,我们应该可以使用logical "or" (||) operatorPackagingUnits | undefined替换为undefined

null

编译器很高兴。综上所述,这是编写相同逻辑的一种round回方式,以便编译器确认其类型安全:

function getPackagingUnitName(key: string): PackagingUnits | null {
  return PackagingUnitsIndexSignature[key] || null;  // okay
}

好的,希望能有所帮助。祝你好运!

答案 1 :(得分:1)

您尝试过

function getPackagingUnitName(key: keyof typeof PackagingUnits): PackagingUnits | null {
    if (PackagingUnits[key]) {
        return PackagingUnits[key];
    }
    return null
}

当然,以防万一,key是从API或类似的东西获得的。否则,该功能不是真正必需的。如果要进行类型检查以确保键是预定义值之一,只需使用

key: keyof typeof PackagingUnits;