Машинопись `enum` из строки JSON
Есть ли способ сделать перечисление TypeScript совместимым со строками из JSON?
Например:
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]
но это громоздко и, кажется, делает аннотацию типа enum запутанной и вводящей в заблуждение, что противоречит ее цели. 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
С помощьюстроковые литеральные типы другая альтернатива (спасибо @basaret), но чтобы получить желаемое использование типа enum (см. выше), необходимо определить ваши значениядважды: один раз в строковом литеральном типе и один раз в качестве значения (константа или пространство имен):
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
Это работает, но требует много шаблонов, достаточно, чтобы я не использовал его большую часть времени. Сейчас я надеюсь, чтопредложение дляstring enums
в конечном итоге сделает дорожную карту.
Обновление TS 2.1
Новыйkeyof
поиск типа позволяет генерировать строковый литерал из ключей 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
Обновление TS 2.4
В TypeScript 2.4 добавлена поддержка строковых перечислений! Приведенный выше пример становится:
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, это не просто сокращение типа строкового литерала, что было бы действительно правильно. Некоторые проблемы, с которыми я столкнулся:
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 перечисления. :)