将字符串解析为Typescript枚举 [英] Parse string as Typescript Enum
问题描述
给出一个如下所示的枚举:
Given an enum that looks like this:
export enum UsedProduct {
Yes = 'yes',
No = 'no',
Unknown = 'unknown',
}
我想编写一个函数,该函数接受一组字符串文字并返回 UsedProduct
的实例。到目前为止,我编写了这样的函数:
I'd like to write a function that takes a set of string literals and returns an instance of UsedProduct
. So far, I wrote a function like this:
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}`);
}
此实现不是很好,因为我必须重新定义枚举。我该如何重写此函数,这样就不必定义'是'| ‘不’| 未知
?
This implementation isn't great because I have to redefine the possible values of the enum. How can I rewrite this function so that I don't have to define 'yes' | 'no' | 'unknown'
?
推荐答案
TypeScript对您来说并不容易,因此答案不是一成不变的。
TypeScript doesn't make this easy for you so the answer isn't a one-liner.
一个枚举
值,如 UsedProduct.Yes
运行时的字符串或数字文字(在这种情况下为字符串是
),但在编译时将其视为以下内容的子类型字符串或数字文字。因此, UsedProduct.Yes扩展了是
是正确的。不幸的是,给定类型 UsedProduct.Yes
,没有编程方式将类型扩展为是
。 。或给定类型 UsedProduct
,没有编程方式将其扩展为是 | 不 | 未知
。该语言缺少一些您需要执行此操作的功能。
An enum
value like UsedProduct.Yes
is just a string or number literal at runtime (in this case, the string "yes"
), but at compile time it is treated as a subtype of the string or number literal. So, UsedProduct.Yes extends "yes"
is true. Unfortunately, given the type UsedProduct.Yes
, there is no programmatic way to widen the type to "yes"
... or, given the type UsedProduct
, there is no programmatic way to widen it to "yes" | "no" | "unknown"
. The language is missing a few features which you'd need to do this.
有 种方法可以使功能签名像 parseUsedProduct
,但它使用泛型和条件类型要达到此目的:
There is a way to make a function signature which behaves like parseUsedProduct
, but it uses generics and conditional types to achieve this:
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
的值扩展 K
(或者如果 K
是一个联合类型,其中其中一个片段与 E
的任何值都不对应),编译器将给出错误消息。可根据要求提供 Not<>
和 Extractable<>
的工作方式的详细信息。
Basically it takes an enum object type E
and a string-or-number type K
, and tries to extract the property value(s) of E
that extend K
. If no values of E
extend K
(or if K
is a union type where one of the pieces doesn't correspond to any value of E
), the compiler will give an error. The specifics of how Not<>
and Extractable<>
work are available upon request.
对于该功能的实现,您可能需要使用类型断言。像这样的东西:
As for the implementation of the function you will probably need to use a type assertion. Something like:
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
进行硬编码:
That should work. In your specific case we can hardcode 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
希望有帮助。祝你好运!
Hope that helps. Good luck!
这篇关于将字符串解析为Typescript枚举的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!