網站搜尋

在 TypeScript 中將 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)];
}

有可能或有一點變通方法可以將枚舉定義為參數嗎?

答案#1

無法確保該參數是枚舉,因為 TypeScript 中的枚舉不會繼承自共同的祖先或介面。

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)無法理解這種語法。

答案#2

你可以做得比 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);

答案#3

總結前面的回答並加入一些新的語法——一個通用的類型安全函數,既適用於數字枚舉,也適用於字串枚舉:

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];
}

答案#4

我同意 @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 列舉擁有的非常特定的鍵結構)。

所以除非你真的需要這樣一個「通用」函數,否則應該為你實際需要的各個 enum 類型(或像 E1|E2|E3 這樣的聯合類型)製作類型安全的函數。

如果你確實有這個需求(這很可能是一個 X-Y 問題,根據更多上下文可能有更好、完全不同的解決方式),使用 any,因為你無論如何已經離開了類型安全的領域。

答案#5

另一個上面未提及的可能選項是使用實際值。然而,這只有在你知道所有選項時才可能。在我看來,這絕對比任何其他選項都要好。

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