将字符串解析为Typescript枚举 [英] Parse string as Typescript Enum

查看:115
本文介绍了将字符串解析为Typescript枚举的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出一个如下所示的枚举:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆