【问题标题】:Typescript: Generic function with enums打字稿:带有枚举的通用函数
【发布时间】:2021-11-08 14:49:54
【问题描述】:

我使用几个枚举作为全局参数。

enum color {
    'red' = 'red',
    'green' = 'green',
    'blue' = 'blue',
};
enum coverage {
    'none' = 'none',
    'text' = 'text',
    'background' = 'background',
};

我将所有枚举合并到一个类型myEnums

type myEnums = color | coverage;

现在我想检查和访问枚举的值。例如:

// Returns undefined if the argument value is not a color.
function getColor(value: string): color | undefined{
    if(value in color) return value as color;
    return undefined;
}

因为有几个枚举,我想创建一个通用函数来访问我的所有枚举。我尝试了以下方法:

function getParam<T extends myEnums>(value: string): T | undefined {
    if(value in T) return value as T;
    return undefined;
}

getParam<color>('red', color);        // => should return 'red'
getParam<coverage>('test', coverage); // => should return undefined

但是 Typescript 编译器说: 'T' 仅指一种类型,但在这里用作值。'

所以我在函数中添加了一个参数 list: T,但 Typescript 假定参数 list 的类型为 string(而不是 object)。 'in' 表达式的右侧不能是原语。

function getParam<T extends allEnums>(value: string, list: T): T | undefined {
    if(value in list) return value as T;
    return undefined;
}

那么如何使用T 作为枚举来调用泛型函数?

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    enum 是 TypeScript 中的一种特殊数据结构。

    如果你想限制泛型,你需要使用typeof Enum

    在您的示例中,您期望typeof color | typeof coverage 而不是color|coverage

    因为最后一个是值的联合,所以第一个是枚举的联合。

    考虑这个例子:

    enum color {
        'red' = 'red',
        'green' = 'green',
        'blue' = 'blue',
    };
    enum coverage {
        'none' = 'none',
        'text' = 'text',
        'background' = 'background',
    };
    
    type myEnums = color | coverage;
    
    const hasProperty = <Obj, Prop extends PropertyKey>(obj: Obj, prop: Prop)
        : obj is Obj & Record<Prop, unknown> =>
        Object.prototype.hasOwnProperty.call(obj, prop);
    
    
    
    function getParam<Keys extends PropertyKey, Value, E extends Record<Keys, Value>, Key extends keyof E>(enm: E, key: Key): E[Key]
    function getParam<Keys extends PropertyKey, Value, E extends Record<Keys, Value>, Key extends PropertyKey>(enm: E, key: Key): Value | undefined
    function getParam<E extends typeof color | typeof coverage, Key extends keyof E>(enm: E, key: Key): E[Key]
    function getParam<E extends typeof color | typeof coverage, Key extends string>(enm: E, key: Key): undefined | E
    function getParam<E extends typeof color | typeof coverage | Record<string, unknown>, Key extends keyof E>(enm: E, key: Key): E[Key] | undefined {
        return hasProperty(enm, key) ? enm[key] : undefined
    }
    
    const y = getParam(color, 'red');        // => color.red
    const x = getParam(coverage, 'undefined'); // => undefined
    const x2 = getParam({}, 'undefined'); // => unknown
    
    const higherOrder = (key: string) => getParam(color, key)
    
    const z = higherOrder('red') // typeof color | undefined
    

    Playground

    请记住,我不确定你想如何处理高阶函数,所以我在上一个示例中只返回了 enumundefined 的联合

    至于if(value in T)这一行:T是一个类型,你不能把它当作一个运行时值。

    【讨论】:

    • 关于您的回答的两个问题:(1)可以使用unknown 类型作为参数吗?该函数应返回枚举值(如果存在),否则未定义。例如:let input : unknown = 'blue'; let result = getParam(color, input);。 (2) 是否可以返回类型Partial&lt;keyof typeof color&gt;;(如果是颜色)和Partial&lt;keyof typeof coverage&gt;;等等?当然前提是key存在,否则应该是undefined
    • 完全改变了逻辑。你想制作超级通用的getParam 函数吗?
    • 这来自我的旧 JS 设置(也许看看我的 getter 函数:stackoverflow.com/questions/69129503/…)。我必须检查unknown 的用户输入。另外我不想为我的所有全局参数(我的枚举)创建一个函数。
    • 如果用户输入未知 - 我们可以删除所有 enums 相关逻辑,对吧?
    • @michaelT 进行了更新。你可以传递任何对象。如果它不适合您,请提供您的案例场景和问题
    【解决方案2】:

    我认为关键是正确键入T,你必须告诉 TS,T 是“类枚举定义”对象。我四处寻找,发现这个,或多或少的工作

    我的一个项目中有这样的东西:

    export function parseEnum<E, K extends string>(
      enumDef: { [key in K]: E },
      str: string | undefined
    ): E | undefined {
      if (str && str in enumDef) {
        return enumDef[str as K] as E;
      }
      return undefined;
    }
    

    所以,这就是“普通枚举”的工作原理,您想使用它“枚举”的“总和”。

    幸运的是,由于枚举在 JS&TS 中的实现方式,您可以像这样合并枚举定义对象:

    const myEnums = {...coverage, ...color }
    // typeof myEnums = coverage | color;
    

    它的工作原理:

    const x = parseEnum(myEnums, 'x');
    // typeof x = coverage | color | undefined;
    

    打字稿游乐场here

    【讨论】:

      猜你喜欢
      • 2020-10-27
      • 2018-05-22
      • 1970-01-01
      • 1970-01-01
      • 2020-04-30
      • 2020-09-06
      • 1970-01-01
      • 1970-01-01
      • 2018-07-05
      相关资源
      最近更新 更多