将字符串解析为Typescript枚举

时间:2018-09-17 14:56:36

标签: typescript enums

给出一个看起来像这样的枚举:

Caused by: java.security.cert.CertPathValidatorException: signature check failed
    at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:135) ~[na:1.8.0_121]
    at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:219) ~[na:1.8.0_121]
    at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:140) ~[na:1.8.0_121]
    at sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:79) ~[na:1.8.0_121]
    at java.security.cert.CertPathValidator.validate(CertPathValidator.java:292) ~[na:1.8.0_121]
    at sun.security.validator.PKIXValidator.doValidate(PKIXValidator.java:347) ~[na:1.8.0_121]
    ... 79 common frames omitted
Caused by: java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPublicKeyImpl

我想编写一个函数,该函数接受一组字符串文字并返回export enum UsedProduct { Yes = 'yes', No = 'no', Unknown = 'unknown', } 的实例。到目前为止,我编写了这样的函数:

UsedProduct

此实现不是很好,因为我必须重新定义枚举的可能值。如何重写此函数,而不必定义export function parseUsedProduct(usedProdStr: 'yes' | 'no' | 'unknown'): UsedProduct { switch (usedProdStr) { case 'yes': return UsedProduct.Yes; case 'no': return UsedProduct.No; case 'unknown': return UsedProduct.Unknown; default: return unknownUsedProductValue(usedProdStr); } } function unknownUsedProductValue(usedProdStr: never): UsedProduct { throw new Error(`Unhandled UsedProduct value found ${usedProdStr}`); }

3 个答案:

答案 0 :(得分:6)

TypeScript对您而言并不容易,因此答案并非一帆风顺。

enum这样的UsedProduct.Yes值在运行时只是一个字符串或数字文字(在这种情况下为字符串"yes"),但是在编译时将其视为 subtype 。因此,UsedProduct.Yes extends "yes"是正确的。不幸的是,给定类型UsedProduct.Yes,没有编程方式将类型扩展为"yes" ......,或者给定类型UsedProduct,没有编程方式将其扩展为{ {1}}。该语言缺少您需要执行的一些功能。

一种方法来制作功能类似于"yes" | "no" | "unknown"的函数签名,但是它使用genericsconditional types来实现:

parseUsedProduct

基本上,它采用枚举对象类型type Not<T> = [T] extends [never] ? unknown : never type Extractable<T, U> = Not<U extends any ? Not<T extends U ? unknown : never> : never> declare function asEnum<E extends Record<keyof E, string | number>, K extends string | number>( e: E, k: K & Extractable<E[keyof E], K> ): Extract<E[keyof E], K> const yes = asEnum(UsedProduct, "yes"); // UsedProduct.yes const no = asEnum(UsedProduct, "no"); // UsedProduct.no const unknown = asEnum(UsedProduct, "unknown"); // UsedProduct.unknown const yesOrNo = asEnum(UsedProduct, Math.random()<0.5 ? "yes" : "no"); // UsedProduct.yes | UsedProduct.no const unacceptable = asEnum(UsedProduct, "oops"); // error 和字符串或数字类型E,并尝试提取扩展{{1 }}。如果没有K的值扩展E(或者如果K是并集类型,其中其中一个不对应任何E的值),则编译器将给出一个错误。 KK的工作原理可根据要求提供。

关于功能的实现,您可能需要使用type assertion。像这样:

E

应该可以。在您的特定情况下,我们可以对Not<>进行硬编码:

Extractable<>

希望有帮助。祝你好运!

答案 1 :(得分:0)

您可以使用ts-enum-util中的getKeyOrThrow方法。不知道它是如何实现的,但是您可以查看它here

Here's a stackblitz是为了演示您的情况。

答案 2 :(得分:0)

使用Typescript 4.1,可以用更简单的方式完成

type UnionToEnum<E extends string, U extends `${E}`> = {
  [enumValue in E as `${enumValue & string}`]: enumValue
}[U]

enum UsedProduct {
  Yes = 'yes',
  No = 'no',
  Unknown = 'unknown',
}

function parseUsedProduct<K extends `${UsedProduct}`>(k: K): UnionToEnum<UsedProduct, K> {
  if (Object.values(UsedProduct).indexOf(k as UsedProduct) < 0)
    throw new Error("Expected one of " + Object.values(UsedProduct).join(", "));
  return k as UsedProduct as UnionToEnum<UsedProduct, K>;
}

// x is of type UsedProduct.Yes
let x = parseUsedProduct('yes');
// error
let c = parseUsedProduct('noo');

playground

这里的键是`${UsedProduct}`,它删除枚举值的“枚举”并将其转换为字符串文字。

注意:仅适用于字符串枚举值,不适用于数字枚举值。