typescript - 将字符串解析为 Typescript 枚举
问题描述
给定一个看起来像这样的枚举:
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}`);
}
这个实现不是很好,因为我必须重新定义枚举的可能值。我怎样才能重写这个函数,这样我就不必定义了'yes' | 'no' | 'unknown'
?
解决方案
type UsedProductType = `${UsedProduct}`;
TS-4.1 之前的答案:
TypeScript 对您来说并不容易,因此答案不是单行的。
enum
像这样的值UsedProduct.Yes
在运行时只是一个字符串或数字文字(在本例中为 string "yes"
),但在编译时它被视为字符串或数字文字的子类型。所以,UsedProduct.Yes extends "yes"
是真的。不幸的是,给定 type UsedProduct.Yes
,没有编程方式将类型扩大到"yes"
... 或者,给定 type UsedProduct
,没有编程方式将其扩大到"yes" | "no" | "unknown"
. 该语言缺少一些您需要执行此操作的功能。
有一种方法可以使函数签名的行为类似于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
和一个字符串或数字类型K
,并尝试提取该E
扩展的属性值K
。如果没有E
extend值K
(或者如果K
是联合类型,其中一个部分不对应于 的任何值E
),编译器将给出错误。可根据要求提供有关如何Not<>
和Extractable<>
工作的详细信息。
至于函数的实现,您可能需要使用类型断言。就像是:
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> {
// runtime guard, shouldn't need it at compiler time
if (Object.values(e).indexOf(k) < 0)
throw new Error("Expected one of " + Object.values(e).join(", "));
return k as any; // assertion
}
那应该行得通。在您的具体情况下,我们可以硬编码UsedProduct
:
type Not<T> = [T] extends [never] ? unknown : never
type Extractable<T, U> = Not<U extends any ? Not<T extends U ? unknown : never> : never>
function parseUsedProduct<K extends string | number>(
k: K & Extractable<UsedProduct, K>
): Extract<UsedProduct, K> {
if (Object.values(UsedProduct).indexOf(k) < 0)
throw new Error("Expected one of " + Object.values(UsedProduct).join(", "));
return k as any;
}
const yes = parseUsedProduct("yes"); // UsedProduct.yes
const unacceptable = parseUsedProduct("oops"); // error
希望有帮助。祝你好运!
推荐阅读
- oracle - 如何将多个结果连接到oracle中的一列?
- c# - 如何使用按钮更改表单的背景颜色
- plot - 为什么 Mathematica 显示一个空白框?
- php - PHP Singleton 作为 HTTP 服务器中 HTTP 请求的回调
- python - Python:将数据从 excel 转换为饼图
- android - Firestore Stripe 错误“无法序列化对象。不支持序列化集合,请改用列表。”
- android - HttpURLConnection 间歇性返回错误代码 500
- scala - 使用不同的设置两次运行 sbt 任务
- node.js - 带有令牌的 PASSPORT GOOGLE OAUTH2
- node.js - 使用快递的 POST 请求