在 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
}
