2016-03-02 88 views
13

有沒有辦法讓TypeScript的枚舉與來自JSON的字符串兼容?來自JSON字符串的Typescript`enum`

例如:

enum Type { NEW, OLD } 

interface Thing { type: Type } 

let thing:Thing = JSON.parse('{"type": "NEW"}'); 

alert(thing.type == Type.NEW); // false 

我會像thing.type == Type.NEW是真實的。或者更具體地說,我希望我可以指定enum值定義爲字符串,而不是數字。

我知道,我可以使用thing.type.toString() == Type[Type.NEW],但這很麻煩,似乎使枚舉類型註釋混淆和誤導,這打破了它的目的。 JSON在技術上是而不是提供了一個有效的枚舉值,所以我不應該輸入屬性枚舉。

那麼,我現在做的,而不是使用字符串類型與靜態常量:

const Type = { NEW: "NEW", OLD: "OLD" } 

interface Thing { type: string } 

let thing:Thing = JSON.parse('{"type": "NEW"}'); 

alert(thing.type == Type.NEW); // true 

這讓我我想要的用法,但類型標註string是太寬泛,容易出錯。

我有點驚訝,JavaScript的超集沒有基於字符串的枚舉。我錯過了什麼嗎?有沒有不同的方式可以做到這一點?


更新TS 1.8

使用string literal types是另一種選擇(感謝@basaret),但以獲得所需的枚舉類使用(上圖)它需要定義兩次你的價值觀:在一次一個字符串字面型,一次作爲值(常量或命名空間):

type Type = "NEW" | "OLD"; 
const Type = { 
    NEW: "NEW" as Type, 
    OLD: "OLD" as Type 
} 

interface Thing { type: Type } 

let thing:Thing = JSON.parse(`{"type": "NEW"}`); 

alert(thing.type === Type.NEW); // true 

這工作,但需要大量的樣板,以至於我不使用最多的IT運時間。現在我希望proposal for string enums最終能夠制定路線圖。


更新TS 2.1

keyof type lookup允許從一個const或命名空間,這使得定義一個較少冗餘的鍵所產生的字符串文字類型:

namespace Type { 
    export const OLD = "OLD"; 
    export const NEW = "NEW"; 
} 
type Type = keyof typeof Type; 

interface Thing { type: Type } 

const thing: Thing = JSON.parse('{"type": "NEW"}'); 
thing.type == Type.NEW // true 

更新T S 2.4

TypeScript 2.4 added support for string enums!上面的例子就變成:

enum Type { 
    OLD = "OLD", 
    NEW = "NEW" 
} 

interface Thing { type: Type } 
const thing: Thing = JSON.parse('{"type": "NEW"}'); 
alert(thing.type == Type.NEW) // true 

這看起來完美的,但還是有一些心痛:

  • 仍然有寫值的兩倍,即OLD = "OLD",而且也沒有確認活動你沒有一個錯字,如NEW = "MEW" ......這已經讓我陷入了真實的代碼。
  • 有一些古怪的東西(也許是錯誤?)與如何檢查枚舉類型,它不只是一個字符串文字類型的簡寫,這是真正正確的。我已經碰到了一些問題:

    enum Color { RED = "RED", BLUE = "BLUE", GREEN = "GREEN" } 
    
    type ColorMap = { [P in Color]: number; } 
    
    declare const color: Color; 
    declare const map: ColorMap; 
    map[color] // Error: Element implicitly has an 'any' type because type 'ColorMap' has no index signature. 
    
    const red: Color = "RED"; // Type '"RED"' is not assignable to type 'Color'. 
    const blue: Color = "BLUE" as "RED" | "BLUE" | "GREEN"; // Error: Type '"RED" | "BLUE" | "GREEN"' is not assignable to type 'Color'. 
    

    enum Color等效代碼由字符串字面類型做工精細取代...

是啊,我覺得我有這個強迫症,我只是想我完美的JS枚舉。 :)

回答

6

如果您正在使用打字稿2.4發佈之前,有一種方法鑄造的枚舉值來any實現與枚舉。

example of your first implementation

enum Type { 
    NEW = <any>"NEW", 
    OLD = <any>"OLD", 
} 

interface Thing { type: Type } 

let thing:Thing = JSON.parse('{"type": "NEW"}'); 

alert(thing.type == Type.NEW); // true 

Typescript 2.4 has built in support for string enums了,所以要轉換成any就沒有存在的必要,你可以實現它,而無需使用String Literal Union Type,這是確定的驗證和自動完成,但不是那麼好可讀性和重構,取決於使用情況。

+0

謝謝!我希望我以前知道這個「任何」的斷言。現在,我正在嘗試TS 2。4個字符串枚舉,它與我原本想要的非常接近......但是我發現了TS類型檢查它的一些問題...... – Aaron

+0

@Aaron很酷,很高興幫助!另外,你可能想要檢查[ts-enums](https://github.com/LMFinney/ts-enums)項目,因爲它使枚舉處理對於很多用例非常靈活和強大 –

0

但類型註釋字符串是太寬泛和容易出錯。

同意。一個快速的解決方法(如果你有代碼生成的奢侈,你可以自動此):

interface Thing { type: "NEW" | "OLD" } 

這些被稱爲字符串文字聯合。更多:https://basarat.gitbooks.io/typescript/content/docs/tips/stringEnums.html

+0

這看起來很有希望。我們將調查升級到TS 1.8的時間。我正在玩它,並無法弄清楚如何使用字符串字面值作爲常量,即類似'thing.type == Type.NEW'。 – Aaron

+1

[這是儘可能接近](http://www.typescriptlang.org/Playground#src=type%20Type%20%3D%20%22NEW%22%20%7C%20%22OLD%22%圖3B%0Aconst%20Type%20%3D%20%7B%0A%09NEW%3A%20%22NEW%22%2C%0A%09OLD%3A%20%22OLD%22%0A%7D%0A%0Ainterface%20Thing% 20%7B%20type%3A%20Type%20%7D%0A%0Alet%20thing%3AThing%20%3D%20JSON.parse(%60%7B%22type%22%3A%20%22NEW%22%7D%60 )%3B%0A%0Aalert(thing.type%20%3D%3D%3D%20Type.NEW)%3B),但它需要定義Type和值兩次,一次作爲'type'用作一個接口類型註釋,並且再次作爲一個'const'用於我需要這些值的地方。這可以改善嗎? – Aaron

0

我一直在使用轉換函數作爲權宜之計。希望這個線程來決議:https://github.com/Microsoft/TypeScript/issues/1206

enum ErrorCode { 
    Foo, 
    Bar 
} 

interface Error { 
    code: ErrorCode; 
    message?: string; 
} 

function convertToError(obj: any): Error { 
    let typed: Error = obj as Error; 

    // Fix any enums 
    typed.code = ErrorCode[typed.code.toString()]; 
    return typed; 
}