Есть ли способ иметь переименование 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), но для получения желаемого использования перечисления (выше) требуется определение ваши значения дважды: один раз в строковом литерале и один раз в качестве значения (константа или пространство имен):
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
позволяет генерировать тип строкового литерала из ключей константы или пространства имен, что делает определение немного менее избыточное:
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 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
, замененный строковыми литералами, работает нормально...
Да, я думаю, что у меня есть OCD, я просто хочу, чтобы мои идеальные JS перечисления.:)