Разница между перечислениями строк и строковыми литералами в TS

Предполагая, что я хочу убедиться, что myKey в { myKey: '' } содержит только строки foo, bar, baz, я мог бы достичь этого двумя способами.

   // with a String Literal Type 
   type MyKeyType = 'foo' | 'bar' | 'baz';

    // or with a String Enum   
    enum MyKeyType {
       FOO = 'foo',
       BAR = 'bar',
       BAZ = 'baz'
    }

Интересно, где все плюсы и минусы одного над другим, так как оба выглядят одинаково для меня (кроме того, как я мог бы получить доступ к значениям, например, при проверке состояния).

Единственное отличие, которое я нашел в документации TS: Enums - это реальные объекты во время выполнения, что может быть желательно в некоторых случаях.

Ответы

Ответ 1

Главное, чтобы понять, что значения перечислений строк непрозрачны.

Предполагаемый прецедент для перечисления строк состоит в том, что вы не хотите, чтобы другой код знал или заботился о том, что поддерживает литеральная строка MyKeyType.FOO. Это означает, что вы не сможете, скажем, передать MyKeyType строку "bar" функции, принимающей MyKeyType.BAR вместо этого вам придется писать MyKeyType.BAR.

Ответ 2

Одним из преимуществ перечисления во время разработки является то, что вы легко увидите список опций с помощью intellisense:

enter image description here

Точно так же вы можете легко изменить значение перечисления, используя инструменты рефакторинга, вместо того, чтобы везде менять строку.

Редактировать: В VS 2017 и последней версии TypeScript intellisense работает со строковыми литералами:

enter image description here

Ответ 3

Что ж, есть разница между строковыми перечислениями и литеральными типами в переданном коде.

Сравните код Typescript

// with a String Literal Type 
type MyKeyType1 = 'foo' | 'bar' | 'baz';

// or with a String Enum   
enum MyKeyType2 {
   FOO = 'foo',
   BAR = 'bar',
   BAZ = 'baz'
}

С переданным кодом JavaScript

// or with a String Enum   
var MyKeyType2;
(function (MyKeyType2) {
    MyKeyType2["FOO"] = "foo";
    MyKeyType2["BAR"] = "bar";
    MyKeyType2["BAZ"] = "baz";
})(MyKeyType2 || (MyKeyType2 = {}));

Что вы можете видеть, так это то, что нет никакого сгенерированного кода для строкового литерала. Потому что Typescripts Transpiler использует только для безопасности типов при переносе. Во время выполнения строковые литералы "генерируются в тупые" строки. Нет ссылок между определением литерала и употреблениями.

Таким образом, существует третья альтернатива, называемая const enum

Посмотри на это

// with a String Literal Type 
type MyKeyType1 = 'foo' | 'bar' | 'baz';

// or with a String Enum   
enum MyKeyType2 {
   FOO = 'foo',
   BAR = 'bar',
   BAZ = 'baz'
}

// or with a Const String Enum   
const enum MyKeyType3 {
   FOO = 'foo',
   BAR = 'bar',
   BAZ = 'baz'
}

var a : MyKeyType1 = "bar" 
var b: MyKeyType2 = MyKeyType2.BAR
var c: MyKeyType3 = MyKeyType3.BAR

будет перенесен в

// or with a String Enum   
var MyKeyType2;
(function (MyKeyType2) {
    MyKeyType2["FOO"] = "foo";
    MyKeyType2["BAR"] = "bar";
    MyKeyType2["BAZ"] = "baz";
})(MyKeyType2 || (MyKeyType2 = {}));
var a = "bar";
var b = MyKeyType2.BAR;
var c = "bar" /* BAR */;

Для дальнейшей игры вы можете проверить эту ссылку

Я предпочитаю const enum case из-за удобного способа ввода Enum.Value. Typescript сделает все возможное, чтобы получить максимальную производительность при переносе.