枚举中的枚举作为参数

Syn*_*oon 36 enums typescript

是不是可以将参数类型设置为枚举?像这样:

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

如果我这样做,Intellij会将此代码标记为未知.并建议重命名变量,这有意义吗?

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

本规范运作正常.但不像优雅和代码方便.

是否有可能或稍微解决方法将枚举定义为参数?

编辑:

在研究了这些答案之后,我是否可以使用一组定义的枚举来做到这一点,类似于enum1 | enum2 | enum3?

Pal*_*leo 17

不可能确保参数是枚举,因为TS中的枚举不会从公共祖先或接口继承.

TypeScript带来了静态分析.您的代码使用动态规划与Object.keyse[dynamicKey].对于动态代码,类型any很方便.

您的代码有错误:length()不存在,e[Math.floor((Math.random() * length)+1)]返回字符串或整数,并且可以手动设置枚举值 ...

这是一个建议:

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));
Run Code Online (Sandbox Code Playgroud)

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

  • 这是可能的,一些额外的类似“枚举”的类型通过过滤器。但如果它的说话方式像“enum”并且工作方式像“enum”,为什么会有不同呢?只是 `&lt;Enum extends Record&lt;string, string | 数字 &gt;&gt;(e: 枚举) =&gt; { ... };` (2认同)

Rya*_*ugh 17

你可以做得比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);
Run Code Online (Sandbox Code Playgroud)

  • 数字索引器不限制非数字键类型 (4认同)

Val*_*kov 14

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

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];
}
Run Code Online (Sandbox Code Playgroud)


Thi*_*ilo 10

我同意@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))];
}
Run Code Online (Sandbox Code Playgroud)

这适用于所有枚举(没有自定义初始值设定项),但它也会接受其他数组作为输入(然后失败,因为方法体依赖于TypeScript枚举所具有的非常特定的键结构).

因此,除非您真正需要这样的"通用"函数,否则请为您实际需要的各个枚举类型(或类似的联合类型E1|E2|E3)创建类型安全函数.

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

  • 它应该是`interface Enum {[key: number]: string | 实际上是数字}` (4认同)

小智 6

也许这个技巧适合:

enum AbstractEnum { // put somewhere in hidden scope
}

private getRandomElementOfEnum(e: typeof AbstractEnum) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个,您可以通过将其定义为“private getRandomElementOfEnum&lt;T extends typeof AbstractEnum&gt;(e: T): T[keyof T] {...}”来进一步加强函数的类型。 (2认同)

Nik*_*nin 6

在 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
Run Code Online (Sandbox Code Playgroud)

用法

枚举类型:
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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud) 枚举:
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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

测试

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
})
Run Code Online (Sandbox Code Playgroud)

  • 你能提供一些简单的解释吗?对于经验不足的其他用户来说不仅有用,而不仅仅是代码 (4认同)

小智 5

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

    doSomething(a: string, b: 'this'|'can'|'work'): void {
     //do something
    }
Run Code Online (Sandbox Code Playgroud)

  • 实际上你可以使用它作为 b 的类型: `b: keyof typeof MyEnumType` (6认同)
  • 是的,当然比任何解决方案都好,但是这个解决方案很难维护。每次我们编辑枚举的值时,我们都需要记住我们在哪里使用它并重构这些参数。 (2认同)
  • @PabloMartínez 这就是这个问题的最佳答案。发布作为答案 (2认同)