【问题标题】:Enum as Parameter in TypeScript枚举作为 TypeScript 中的参数
【发布时间】:2015-08-26 19:11:05
【问题描述】:

不可以将参数的类型设置为 Enum 吗?像这样:

private getRandomElementOfEnum(e : enum):string{
    var length:number = Object.keys(e).length;
    return e[Math.floor((Math.random() * length)+1)];
}

编译抛出以下错误:

需要参数表达式。(1135)

any 显然一切都很好:

private getRandomElementOfEnum(e : any):string{
    var length:number = Object.keys(e).length;
    return e[Math.floor((Math.random() * length)+1)];
}

此代码运行良好。但没有那么优雅和类型安全。

是否有可能将枚举定义为参数?

【问题讨论】:

  • 不应该是e: MyEnumType吗?或者你想接受任何类型的枚举。那有什么好处呢?你也可以e: MyEnum|MyOtherEnum|MyThirdEnum.
  • 相关(但可能已过时):stackoverflow.com/questions/21293063/…
  • 每个枚举都应该是可能的,所以我可以通过调用这个函数得到每个枚举的随机元素。
  • 你的秒解决方案@Thilo 工作但不是很方便,它应该适用于每个枚举。
  • @Thilo,不。 MyEnum 作为一种类型提供其值类型。如果您希望枚举本身被传递,您需要将其视为一个对象。

标签: enums typescript


【解决方案1】:

我遇到了同样的问题,我做了这个

  private getOptionsFromEnum(OptionEnum: Record<string, string>): Array<SelectOption> {
    return Object.keys(OptionEnum).map((value) => {
      return {
        name: OptionEnum[value],
        value,
      } as SelectOption;
    });
  }

【讨论】:

    【解决方案2】:

    在 TypeScript 3.9.7 上测试

    解决方案

    type EnumTypeString<TEnum extends string> =
        { [key in string]: TEnum | string; }
    
    type EnumTypeNumber<TEnum extends number> =
        { [key in string]: TEnum | number; }
        | { [key in number]: string; }
    
    type EnumType<TEnum extends string | number> =
        (TEnum extends string ? EnumTypeString<TEnum> : never)
        | (TEnum extends number ? EnumTypeNumber<TEnum> : never)
    
    type EnumOf<TEnumType> = TEnumType extends EnumType<infer U>
        ? U
        : never
    

    用法

    枚举类型:
    function forEachEnum<TEnum extends string | number>(
        enumType: EnumType<TEnum>,
        callback: (value: TEnum, key: string) => boolean|void,
    ) {
        for (let key in enumType) {
            if (Object.prototype.hasOwnProperty.call(enumType, key) && isNaN(Number(key))) {
                const value = enumType[key] as any
                if (callback(value, key)) {
                    return
                }
            }
        }
    }
    
    枚举:
    function forEachEnum2<TEnumType>(
        enumType: TEnumType,
        callback: (value: EnumOf<TEnumType>, key: string) => boolean|void,
    ) {
        for (let key in enumType) {
            if (Object.prototype.hasOwnProperty.call(enumType, key) && isNaN(Number(key))) {
                const value = enumType[key] as any
                if (callback(value, key)) {
                    return
                }
            }
        }
    }
    

    测试

    enum EnumAsString {
        Value1 = 'value 1',
        Value2 = 'value 2',
    }
    
    enum EnumAsNumber {
        Value1 = 1,
        Value2 = 2,
    }
    
    // Error
    let sn: EnumType<string> = EnumAsNumber
    
    // Correct
    let ns: EnumType<number> = EnumAsString // I have not found a solution for the error here
    let nn: EnumType<number> = EnumAsNumber
    let Nn: EnumType<EnumAsNumber> = EnumAsNumber
    let ss: EnumType<string> = EnumAsString
    let Ss: EnumType<EnumAsString> = EnumAsString
    
    forEachEnum(EnumAsString, value => {
        let e: EnumAsString = value
        let s: string = value
        let n: number = value // Error
    })
    
    forEachEnum(EnumAsNumber, value => {
        let e: EnumAsNumber = value
        let s: string = value // Error
        let n: number = value
    })
    
    forEachEnum2(EnumAsString, value => {
        let e: EnumAsString = value
        let s: string = value
        let n: number = value // Error
    })
    
    forEachEnum2(EnumAsNumber, value => {
        let e: EnumAsNumber = value
        let s: string = value // Error
        let n: number = value
    })
    

    【讨论】:

    • 你能提供一些简单的解释吗?对没有经验的其他用户有用,而不仅仅是代码
    【解决方案3】:

    这是一个示例,它允许使用泛型传递具有该枚举的类型检查值的枚举。这实际上是对此处标记为重复的稍微不同的问题的回应:Typescript how to pass enum as Parameter

    enum Color {
        blue,
    };
    enum Car {
        cadillac,
    };
    enum Shape {
        square,
    }
    
    type SupportedEnums = typeof Color | typeof Car;
    
    type InvertTypeOf<T> = T extends typeof Color ? Color :
        T extends typeof Car ? Car : never;
    
    function getText<T extends SupportedEnums>(enumValue: InvertTypeOf<T>, typeEnum: T) string | undefined {
      if (typeEnum[enumValue]) {
        return `${enumValue}(${typeEnum[enumValue]})`;
      }
    }
    
    console.log(getText(Car.cadillac, Car)); // 0(cadillac)
    console.log(getText(0, Color)); // 0(red)
    console.log(getText(4, Color)); // undefined
    
    // @ts-expect-error Color is not Car
    console.log(getText(Color.blue, Car));
    
    // @ts-expect-error Car is not a Color
    console.log(getText(Car.toyota, Color));
    
    // @ts-expect-error  Shape is not in SupportedEnums
    console.log(getText(5, Shape));
    
    // @ts-expect-error  Shape is not in SupportedEnums
    console.log(getText(Shape.square, Shape));
    

    【讨论】:

      【解决方案4】:

      也许这个技巧适合:

      enum AbstractEnum { // put somewhere in hidden scope
      }
      
      private getRandomElementOfEnum(e: typeof AbstractEnum) {
          ...
      }
      

      【讨论】:

      • 我喜欢这个,你可以通过定义为private getRandomElementOfEnum&lt;T extends typeof AbstractEnum&gt;(e: T): T[keyof T] {...}来进一步加强函数的类型化。
      【解决方案5】:

      用一些新语法总结以前的答案 - 一个通用类型安全函数,它适用于数字枚举和字符串枚举:

      function getRandomElementOfEnum<T extends {[key: number]: string | number}>(e: T): T[keyof T] {
        const keys = Object.keys(e);
      
        const randomKeyIndex = Math.floor(Math.random() * keys.length);
        const randomKey = keys[randomKeyIndex];
      
        // Numeric enums members also get a reverse mapping from enum values to enum names.
        // So, if a key is a number, actually it's a value of a numeric enum.
        // see https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings
        const randomKeyNumber = Number(randomKey);
        return isNaN(randomKeyNumber)
          ? e[randomKey as keyof T]
          : randomKeyNumber as unknown as T[keyof T];
      }
      

      【讨论】:

      • 这应该是公认的答案,完美的解决方案,非常感谢
      • 唯一的小问题是,它不限于枚举。也可以将{ p1: true, p2: { f: 8}} 传递给它。
      • 我能想到的最安全的类型是Record&lt;string | number, string&gt; | Record&lt;string, number&gt;。它仍然没有捕获 { p1: 5, p2, "f" },但还会刷新一些意外输入。
      【解决方案6】:

      @selinathat 的解决方案只有在您的类型很少时才会很棒。但是如果我们有更多呢?例如:

      doSomething(a: string, b: 'this'|'can'|'work'|'test1'|'test2'|'test3'): void {
       //do something
      }
      

      它很丑哈!? 我更喜欢使用 keyof

      interface Items {
          'this',
          'can',
          'work',
          'test1',
          'test2',
          'test3',
      }
      
      doSomething(a: string, b: keyof Items): void {
       //do something
      }
      

      【讨论】:

      • 在他的回答中查看我的评论:b: keyof typeof MyEnumType
      【解决方案7】:

      上面没有提到的另一个可能的选项是使用实际值。然而,只有当您知道所有选项时,这才有可能。在我看来,这绝对比任何一个都好。

          doSomething(a: string, b: 'this'|'can'|'work'): void {
           //do something
          }
      

      【讨论】:

      • 是的,当然比任何方法都好,但是这种解决方案很难维护。每次我们编辑枚举的值时,我们都需要记住我们在哪里使用它并重构这些参数。
      • 你救了我
      • 其实可以用这个作为b的类型:b: keyof typeof MyEnumType
      • @PabloMartínez 这是这个问题的最佳答案。作为答案发布
      • @Ayyappa:这取决于枚举的类型。如果枚举是一个字符串映射,它将不起作用。像enum MyEnum { A = "admin", U = "user"} 一样不幸的是不起作用。
      【解决方案8】:

      我同意@Tarh。 TypeScript 中的枚举只是没有通用接口或原型的 Javascript 对象(如果它们是 const enum,那么它们甚至都不是对象),因此您不能将类型限制为“任何枚举”。

      我能得到的最接近的是以下内容:

      enum E1 {
          A, B, C
      }
      enum E2 {
          X, Y, Z
      }
      
      // make up your own interface to match TypeScript enums
      // as closely as possible (not perfect, though)
      interface Enum {
          [id: number]: string
      }
      
      function getRandomElementOfEnum(e: Enum): string {
         let length = Object.keys(e).length / 2;
         return e[Math.floor((Math.random() * length))];
      }
      

      这适用于所有枚举(没有自定义初始化程序),但它也可以接受其他数组作为输入(然后由于方法体依赖于 TypeScript 枚举所具有的非常特定的键结构而失败)。

      因此,除非您确实需要这样一个“通用”函数,否则请为您实际需要的各个枚举类型(或联合类型,如 E1|E2|E3)创建类型安全函数。

      如果你确实有这种需求(这很可能是一个 XY 问题,可以在更多上下文的情况下以更好、完全不同的方式解决),请使用 any,因为无论如何你已经离开了类型安全领域。

      【讨论】:

      • 我没有测试过这个理论,但我很确定你的实现不支持enum E3 { X = 4, Y, Z }
      • 实际上应该是interface Enum {[key: number]: string | number}
      【解决方案9】:

      无法确保参数是枚举,因为 TS 中的枚举不继承自共同的祖先或接口。

      TypeScript 带来了静态分析。您的代码使用带有Object.keyse[dynamicKey] 的动态编程。对于动态代码,any 类型比较方便。

      您的代码有问题:length() 不存在,e[Math.floor((Math.random() * length)+1)] 返回字符串或整数,the enumeration values can be manually set...

      这是一个建议:

      function getRandomElementOfEnum<E>(e: any): E {
          var keys = Object.keys(e),
              index = Math.floor(Math.random() * keys.length),
              k = keys[index];
          if (typeof e[k] === 'number')
              return <any>e[k];
          return <any>parseInt(k, 10);
      }
      
      function display(a: Color) {
          console.log(a);
      }
      
      enum Color { Blue, Green };
      display(getRandomElementOfEnum<Color>(Color));
      

      理想情况下,参数类型 any 应替换为 typeof E,但编译器 (TS 1.5) 无法理解此语法。

      【讨论】:

      • 呵呵,我的代码其实是bug,10分钟后才发现,这里忘记改代码了..
      • 这是可能的,有一些额外的enum-like 类型通过过滤器。但如果它像enum 那样说话并且像enum 那样工作,为什么会有所不同呢?就&lt;Enum extends Record&lt;string, string | number&gt;&gt;(e: Enum) =&gt; { ... };
      【解决方案10】:

      你可以比any做得更好:

      enum E1 {
          A, B, C
      }
      enum E2 {
          X, Y, Z
      }
      
      function getRandomElementOfEnum(e: { [s: number]: string }): number {
          /* insert working implementation here */
          return undefined;
      }
      
      // OK
      var x: E1 = getRandomElementOfEnum(E1);
      // Error
      var y: E2 = getRandomElementOfEnum(window);
      // Error
      var z: string = getRandomElementOfEnum(E2);
      

      【讨论】:

      • 编译器假定E1 匹配{ [s: number]: string } 类型,但它是常年的吗?对象E1 包含字符串键。
      • 数字索引器不限制非数字键类型
      • 编译器将其编译为:function getRandomElementOfEnum(e) { /* insert working implementation here */ return undefined; }
      • 请注意,即使 TS 进行反向映射并且人们​​可能会将这些枚举视为“E1 的值为 0 的键“A””,[s: string]: number 不起作用。
      猜你喜欢
      • 2020-10-26
      • 2020-08-05
      • 2012-03-27
      • 1970-01-01
      • 2011-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多