【问题标题】:How to get a random enum in TypeScript?如何在 TypeScript 中获取随机枚举?
【发布时间】:2023-03-06 22:23:01
【问题描述】:

如何从枚举中获取随机项?

enum Colors {
  Red, Green, Blue
}

function getRandomColor(): Color {
  // return a random Color (Red, Green, Blue) here
}

【问题讨论】:

    标签: typescript enums


    【解决方案1】:

    在从其他解决方案获得大量灵感keyof 关键字之后,这里是一个返回类型安全随机枚举通用方法 .

    function randomEnum<T>(anEnum: T): T[keyof T] {
      const enumValues = Object.keys(anEnum)
        .map(n => Number.parseInt(n))
        .filter(n => !Number.isNaN(n)) as unknown as T[keyof T][]
      const randomIndex = Math.floor(Math.random() * enumValues.length)
      const randomEnumValue = enumValues[randomIndex]
      return randomEnumValue;
    }
    

    像这样使用它:

    interface MyEnum {X, Y, Z}
    const myRandomValue = randomEnum(MyEnum) 
    

    myRandomValue 将是 MyEnum 类型。

    【讨论】:

    • 你不能用Object.keys(anEnum).map(n =&gt; Number.parseInt(n))代替Object.values(anEnum).map(n =&gt; +n)吗?枚举的键都是字符串,而它的值是数字和字符串的混合(数字是实际的枚举值,字符串是它的键)。例如,给定 enum Bool { FALSE, TRUE }Object.keys(Bool) 将返回 ["0", "1", "FALSE", "TRUE"](所有字符串),但 Object.values(Bool) 将返回 ["FALSE", "TRUE", 0, 1](数字和字符串)。
    • 嗯,对我不起作用,这是基于数字的枚举吗?
    • 这段代码无法编译,有很多错误 1) 'T' 类型的参数不能分配给'object' 类型的参数 2) 'parseInt' 类型不存在于类型'数构造器'。您需要更改目标库吗?尝试将“lib”编译器选项更改为“es2015”或更高版本 3) 类型“NumberConstructor”上不存在属性“isNaN”。您需要更改目标库吗?尝试将“lib”编译器选项更改为“es2015”或更高版本
    • 很可能是 kotlin 改变了它们的语法。我会看看并调整。
    【解决方案2】:

    上面的大部分答案都是返回枚举键,但我们真的不关心枚举值吗?

    如果你使用的是 lodash,这其实很简单:

    _.sample(Object.values(myEnum)) as MyEnum
    

    不幸的是,强制转换是必要的,因为这会返回any 的类型。 :(

    如果您不使用 lodash,或者您希望它作为自己的函数,我们仍然可以通过修改 @Steven Spungin 的答案来获得类型安全的随机枚举:

    function randomEnum<T>(anEnum: T): T[keyof T] {
      const enumValues = (Object.values(anEnum) as unknown) as T[keyof T][];
      const randomIndex = Math.floor(Math.random() * enumValues.length);
      return enumValues[randomIndex];
    }
    

    【讨论】:

    • 您的 lodash 示例中的 myEnum 是什么?
    • Object.values(myEnum) 发生了一些奇怪的事情——它为上面的 Colors 枚举返回了一个混合字符串和数字的数组,例如 ['Red', 'Green', 'Blue', 0, 1, 2]。 Object.keys 的作用相同,只是在这种情况下数字也是字符串:['Red', 'Green', 'Blue', '0', '1', '2']
    【解决方案3】:

    如果您需要支持字符串或异构枚举,我可能会建议这样的函数:

    const randomEnumKey = enumeration => {
      const keys = Object.keys(enumeration)
          .filter(k => !(Math.abs(Number.parseInt(k)) + 1));
      const enumKey = keys[Math.floor(Math.random() * keys.length)];
      return enumKey;
    };
    

    获取随机值:

    const randomEnumValue = enumeration => enumeration[randomEnumKey(enumeration)];
    

    或者简单地说:

    MyEnum[randomEnumKey(MyEnum)]
    

    例子:

    https://stackblitz.com/edit/typescript-random-enum

    【讨论】:

    【解决方案4】:

    这是我能想到的最好的方法,但它看起来像一个 hack,并且取决于 TypeScript 中 enum 的实现,我不确定它是否会保持不变。

    给定一个枚举,例如

    enum Color { Red, Green, Blue }
    

    如果我们console.log 它,我们会得到以下输出。

    {
      '0': 'Red',
      '1': 'Green',
      '2': 'Blue',
      Red: 0,
      Green: 1,
      Blue: 2,
    }
    

    这意味着我们可以遍历该对象的键并仅获取数值,如下所示:

    const enumValues = Object.keys(Color)
      .map(n => Number.parseInt(n))
      .filter(n => !Number.isNaN(n))
    

    在我们的例子中,enumValues 现在是 [0, 1, 2]。我们现在只需要随机选择其中之一。有一个很好的函数实现,它返回一个random integer between two values,这正是我们随机选择索引所需要的。

    const randomIndex = getRandomInt(0, enumValues.length)
    

    现在我们只选择随机枚举值:

    const randomEnumValue = enumValues[randomIndex]
    

    【讨论】:

    • TSLint 给出错误:TS2322: Type 'number' is not assignable to type 'Color' 在你的最后一行
    • 嗯,可能在此期间发生了一些变化,或者我们有不同的编译器标志。不幸的是,我目前无法对其进行测试,而且我想我不记得这是什么版本了。 ://
    • 该死,不用担心:/我自己也在研究一个不同的解决方案——我也在使用 SonarTS 和 TSLint,它有非常严格的规则
    【解决方案5】:

    您可以找到以下代码来完成工作。

    enum Colors {
    Red, blue, pink, yellow, Orange
    }
    function getRandomColor(): string
    {
       var len = (Object.keys(Colors).length/2)-1; // returns the length
       // calculate the random number
       var item = (Math.floor(Math.random() * len) + 0); 
       return Colors[item];
    }
    

    【讨论】:

    • 如果你指定一个像enum Colors { Red: 100, Blue }这样的枚举,这将失败
    • 是的,但我已经给出了相关格式的答案,并且它的工作非常好。
    • @LazarLjubenović 看起来像这样,你可以这样做const item = Math.floor(Math.random() * Object.keys(Colors).length); const i2 = Object.keys(Colors)[item]; return Colors[i2];
    【解决方案6】:

    这个怎么样,使用 es2017 中的Object.values(IE 不支持,其他浏览器支持较新):

    function randEnumValue<T>(enumObj: T): T[keyof T] {
      const enumValues = Object.values(enumObj);
      const i = Math.floor(Math.random() * enumValues.length);
      return enumValues[i];
    }```
    

    【讨论】:

      【解决方案7】:

      除了@sarink 的回答。

      import _ from 'lodash';
      
      export function getRandomValueFromEnum<E>(enumeration: { [s: string]: E } | ArrayLike<E>): E {
        return _.sample(Object.values(enumeration)) as E;
      }
      

      【讨论】:

        【解决方案8】:

        它并不完美,但可以尝试这样的东西..

        function getRandomEnum(input: object): any {
          const inputTransform: { [key: string]: string } = input as { [key: string]: string };
          const keysToArray = Object.keys(inputTransform);
          const valuesToArray = keysToArray.map(key => inputTransform[key]);
          if (!isNaN(parseInt(keysToArray[0], 10))) {
            const len = keysToArray.length;
            while (keysToArray.length !== len / 2) {
              keysToArray.shift();
              valuesToArray.shift();
            }
          }
          const randIndex = Math.floor(keysToArray.length * Math.random());
          return valuesToArray[randIndex];
        }
        

        经过测试...

        enum Names {
          foo,
          bar,
          baz,
          // foo = "fooZZZ",
          // bar = "barZZZ",
          // baz = "bazZZZ",
        }
        
        for (let i = 0; i < 10; i += 1) {
          const result = getRandomEnum(Names);
          if (result === Names.foo) console.log(`${result} is foo`);
          else if (result === Names.bar) console.log(`${result} is bar`);
          else if (result === Names.baz) console.log(`${result} is baz`);
        }
        

        输出...

        0 is foo
        0 is foo
        2 is baz
        0 is foo
        2 is baz
        2 is baz
        1 is bar
        1 is bar
        2 is baz
        1 is bar
        

        或者...

        bazZZZ is baz
        bazZZZ is baz
        bazZZZ is baz
        barZZZ is bar
        bazZZZ is baz
        barZZZ is bar
        barZZZ is bar
        fooZZZ is foo
        barZZZ is bar
        fooZZZ is foo
        

        【讨论】:

          【解决方案9】:

          编辑:固定类型问题。

          稍微整理一下@ShailendraSingh的答案,如果枚举在问题中定义,没有任何自定义索引,那么这是一个简单的方法:

          enum Color {
              Red, Green, Blue
          }
          
          function getRandomColor(): Color {
              var key = Math.floor(Math.random() * Object.keys(Color).length/2);
              return Color[key];
          }
          

          请注意,此处的返回类型Color(根据问题中的要求)实际上是枚举索引而不是颜色名称。

          【讨论】:

          • TS Lint 给出错误:TS2322: Type 'number' is not assignable to type 'Color'
          • 那不是 linter,那是 Typescript 本身。
          【解决方案10】:
          enum Colors {
              Red, Green, Blue
          }
          
          let keys = Object.keys(Colors)
          let real_keys = keys.slice(keys.length / 2,keys.length)
          let random =  real_keys[Math.floor(Math.random()*real_keys.length)]
          console.log(random)
          

          使用 esnext 在 typescript 3.5.2 上测试。很简单。

          使用命名空间的奖励

          enum Colors {
              Red,
              Green,
              Blue,
          }
          namespace Colors {
              // You need to minus the function inside your namespace
              // That's why I had - 1
              // Because instead of 0,1,2,red,green,blue = 6
              // It will be 0,1,2,red,green,blue,Random = 7
              export function Random(): any {
                  let length = ((Object.keys(Colors).length - 1) / 2)
                  return Colors[Math.floor(Math.random() * length / 2)]
              }
          }
          
          console.log(Colors.Random())
          

          【讨论】:

            【解决方案11】:

            这里有一个在 es5 中对我有用的解决方案:

             function getRandomEnum<T extends object>(anEnum: T): T[keyof T] {
                let enumValues = Object.keys(anEnum)
                        .filter(key => typeof anEnum[key as keyof typeof anEnum] === 'number')
                        .map(key => key);
            
                let randomIndex = Math.floor(Math.random() * enumValues.length)
            
                return anEnum[randomIndex as keyof T];
            }
            

            【讨论】:

            • 您能解释一下与其他答案的不同之处以及与 ES5 相关的原因吗?
            • Number.parseInt() 和 Number.isNan() 当我在 TypeScript 中定位 ES5 时,类型“NumberConstructor”上不存在。在签名中省略 时我也遇到了麻烦!如果您的目标大于 ES5,则此问题的解决方案效果很好。我猜 javascript 文件仍然可以工作,但想删除 linter 错误。
            • 为什么这段代码有时会返回一个随机数?
            【解决方案12】:

            适用于所有类型枚举的简单解决方案

            function randomEnum<T extends Record<string, number | string>>(anEnum: T): T[keyof T] {
                const enumValues = getEnumValues(anEnum);
                const randomIndex = Math.floor(Math.random() * enumValues.length);
                    
                return enumValues[randomIndex];
            }
            

            使用lodash,但没有它可以重写函数

            function getEnumValues<T extends Record<string, number | string>>(anEnum: T): Array<T[keyof T]> {
                const enumClone = _.clone(anEnum);
            
                _.forEach(enumClone, (_value: number | string, key: string) => {
                    if (!isNaN(Number(key))) {
                        delete enumClone[key];
                    }
                });
            
                return _.values(enumClone) as Array<T[keyof T]>;
            }
            
            enum Status { active, pending }
            enum Type { active = 'active', pending = 'pending' } 
            enum Mixed { active = 'active', pending = 1 }
            
            randomEnum(Status) // (Return random between 0,1) 
            randomEnum(Type) // (Return 'active' or 'pending' string) 
            randomEnum(Mixed) // (Return 'active' or 1)
            

            【讨论】:

              【解决方案13】:

              所以没有答案对我有用,我最终这样做了:

              具有以下枚举

              export enum Jokenpo {
                PAPER = 'PAPER',
                ROCK = 'ROCK',
                SCISSOR = 'SCISSOR',
              }
              

              随机选择

              const random = Math.floor(Math.random() * Object.keys(Jokenpo).length);
              const selected = Object.values(Jokenpo)[random];
              return Jokenpo[selected];
              

              我只是从枚举中获取一个随机值并使用它来转换为真实的值。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2020-09-24
                • 2015-12-22
                • 1970-01-01
                • 1970-01-01
                • 2011-05-03
                • 1970-01-01
                • 2019-12-30
                • 2018-07-07
                相关资源
                最近更新 更多