web-dev-qa-db-ja.com

typescriptのパラメーターとして列挙

パラメーターの型をEnumに設定することはできませんか?このような:

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

それを行うと、Intellijはこのコードを不明としてマークします。そして、変数の名前を変更することを提案します、それは理にかなっていますか?

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

このコードは正常に機能します。しかし、エレガントでコードに馴染みはありません。

パラメータとして列挙型を定義する可能性または少しの回避策はありますか?

編集:

それらの答えを研究した後、enum1 | enum2 | enum3に似た定義済み列挙型のセットでもこれを行うことができますか?

31
Synoon

TSの列挙は共通の祖先またはインターフェイスを継承しないため、パラメーターが列挙であることを保証することはできません。

TypeScriptは静的分析をもたらします。コードでは、_Object.keys_および_e[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));
_

理想的には、パラメータタイプanyを_typeof E_に置き換える必要がありますが、コンパイラ(TS 1.5)はこの構文を理解できません。

15
Paleo

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);
15
Ryan Cavanaugh

@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などのユニオン型)に対して型保証された関数を作成してください。

そして、もしあなたがこの必要性を持っているなら(そしてこれはコンテキストが多ければより良い、完全に異なる方法で解決できるX-Y問題であるかもしれません)、とにかくタイプセーフな領域を残しているのでanyを使います。

8
Thilo

上記以外のオプションとして、実際の値を使用することもできます。ただし、これはすべてのオプションを知っている場合にのみ可能です。これは、私の意見では、間違いなくどの製品よりも優れています。

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

少し新しい構文-数値列挙型と文字列列挙型で動作する一般的なタイプセーフ関数を使用して、以前の回答を要約します。

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];
}
2
Valeriy Katkov

@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
}
1
MajiD