2016-03-02 17 views
13

Czy istnieje sposób na wybranie wyliczenia TypeScript zgodnego z łańcuchami z JSON?Opis maszynowy `wyliczanie` z ciągu JSON

Na przykład:

enum Type { NEW, OLD } 

interface Thing { type: Type } 

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

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

bym jakthing.type == Type.NEW aby mogło być prawdziwe. A dokładniej, chciałbym móc określić wartości enum, które mają być zdefiniowane jako ciągi, a nie liczby.

Jestem świadomy, że mogę używać thing.type.toString() == Type[Type.NEW], ale jest to uciążliwe i wydaje się, że adnotacja typu wyliczeniowego jest myląca i wprowadzająca w błąd, co jest sprzeczne z jej celem. JSON jest technicznie nie dostarczając prawidłową wartość wyliczenia, więc nie powinienem wpisywać właściwości do wyliczenia.

Więc co mam aktualnie robi zamiast używa typu string ze stałych statycznych:

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

interface Thing { type: string } 

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

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

To dostaje mi użycie chcę, ale typ adnotacji string jest zbyt szeroka i podatne na błędy.

Jestem nieco zaskoczony, że nadzbiór JavaScript nie ma wyrażeń opartych na ciągach znaków. Czy czegoś brakuje? Czy można to inaczej zrobić?


Aktualizacja TS 1,8

Korzystanie string literal types jest inny alternatywny (dzięki @basaret), ale aby uzyskać pożądany użycie enum-like (powyżej) wymaga definiowania wartości dwukrotnie: raz w łańcuch znaków typu, a raz jako wartość (stałą lub nazw):

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 

To działa, ale zajmuje sporo boilerplate, tyle że nie używam go o najbardziej w tym czasie. Na razie mam nadzieję, że proposal for string enums ostatecznie wykona mapę drogową.


Aktualizacja TS 2,1

Nowy keyof type lookup pozwala na ciąg dosłownym typu mają być generowane z klawiszy const lub nazw, co czyni definicji trochę mniej zbędny:

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 

Aktualizacja T S 2.4

TypeScript 2.4 added support for string enums!Powyższy przykład postać:

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

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

To wygląda prawie doskonały, ale jeszcze trochę strapienie:

  • Ty nadal trzeba pisać wartość dwukrotnie, tj OLD = "OLD", i nie ma walidacji że nie masz literówki, jak NEW = "MEW" ... to już mnie ugryzło w prawdziwym kodzie.
  • Istnieje kilka osobliwości (być może błędów?) Z tym, jak sprawdzane jest wyliczenie typu, a nie tylko skrótem typu literowego, co jest naprawdę poprawne. Niektóre kwestie ja zderzyłem:

    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'. 
    

    Odpowiednik kodu z enum Color zastąpiony ciągiem dosłowne rodzaje działać dobrze ...

Tak, myślę, że mam OCD o tym, po prostu chcę moje doskonałe wyliczenia JS. :)

Odpowiedz

6

Jeśli używasz maszynopis przed wydaniem 2.4, istnieje sposób, aby to osiągnąć przez odlewanie z teksty stałe wartości swojej wyliczenia do 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 już, więc obsada do any byłoby już konieczne i można go osiągnąć bez użycia String Literal Union Type, który jest ok dla walidacji i autouzupełniania, ale nie tak dobre dla czytelność i refaktoryzacja, w zależności od scenariusza użycia.

+0

Dzięki! Szkoda, że ​​nie wiedziałem wcześniej o twierdzeniu "any". Teraz próbuję TS 2.4 string wylicza, i jest to dość zbliżone do tego, co pierwotnie chciałem ... ale znalazłem pewne problemy ze sposobem, w jaki sprawdza go typ TS ... – Aaron

+0

@Aaron fajny, z przyjemnością pomogę! Możesz również sprawdzić projekt [ts-enums] (https://github.com/LMFinney/ts-enums), ponieważ sprawia on, że obsługa enum jest bardzo wszechstronna i wydajna w wielu przypadkach użycia –

0

ale ciąg adnotacji typu jest zbyt szeroki i podatny na błędy.

Uzgodnione. Jedno szybkie obejście (jeśli masz luksus generowania kodu można zautomatyzować ten):

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

Są to tak zwane ciąg literały w unii. Więcej: https://basarat.gitbooks.io/typescript/content/docs/tips/stringEnums.html

+0

To wygląda obiecująco. Zbadamy, kiedy dokonamy aktualizacji do wersji TS 1.8. Grałem z nim i nie byłem w stanie opracować sposobu użycia ciągowych wartości literowych jako stałych, np. Coś typu "thing.type == Type.NEW". – Aaron

+1

[To jest tak blisko, jak mogłem dostać] (http://www.typescriptlang.org/Playground#src=type%20Type%20%3D%20%22NEW%22%20%7C%20%22OLD%22% 3B% 0Akonst% 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% 20typ% 3A% 20Rodzaj% 20% 7D% 0A% 0Alet% 20thing% 3Całkowite% 20% 3D% 20JSON.parse (% 60% 7B% 22tyg.% 22% 3A% 20% 22NEW% 22% 7D% 60)% 3B% 0A% 0Aalert (thing.type% 20% 3D% 3D% 3D% 20Type.NEW)% 3B), ale wymaga dwukrotnego zdefiniowania "Typu" i wartości, raz jako "typ" do użycia jako adnotacja typu interfejsu i znowu jako "const" dla zastosowań, w których potrzebuję wartości. Czy można to poprawić? – Aaron

0

Używam funkcji konwertera jako stopgap. Mam nadzieję, że ten wątek dochodzi do uchwały: 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; 
}