Перечисления - это способ организовать коллекцию связанных значений. Многие другие языки программирования (C/C#/Java) имеют тип данных enum
, а JavaScript - нет. Тем не менее, TypeScript имеет. Вот пример описания перечисления в TypeScript:
enum CardSuit {
Clubs,
Diamonds,
Hearts,
Spades
}
// Пример использования
var card = CardSuit.Clubs;
// Предохранитель
card = "not a member of card suit"; // Ошибка : строка не может быть назначена для типа `CardSuit`
Эти значения перечислений являются числами
, поэтому далее я буду называть их числовыми перечислениями.
TypeScript перечисления основанные на числах. Это означает, что числа могут быть назначены на экземпляр перечисления, а также всё, что совместимо с числом
.
enum Color {
Red,
Green,
Blue
}
var col = Color.Red;
col = 0; // Фактически то же что и Color.Red
Прежде чем мы углубимся в перечисления, давайте рассмотрим генерируемый ими JavaScript, вот пример TypeScript:
enum Tristate {
False,
True,
Unknown
}
генерирует следующий JavaScript:
var Tristate;
(function (Tristate) {
Tristate[Tristate["False"] = 0] = "False";
Tristate[Tristate["True"] = 1] = "True";
Tristate[Tristate["Unknown"] = 2] = "Unknown";
})(Tristate || (Tristate = {}));
давайте сосредоточимся на строке Tristate[Tristate["False"] = 0] = "False";
. Внутри неё выражение Tristate["False"] = 0
не требует особых объяснений, по факту оно присваивает свойству "False"
объекта Tristate
значение 0
. Обратите внимание, что в JavaScript оператор присваивания возвращает присвоенное значение (в данном случае 0
). Поэтому следующая операция, выполняемая средой выполнения JavaScript, это Tristate[0] = "False"
. Это означает, что вы можете использовать переменную Tristate
для преобразования строковой версии перечисления в число или числовой версии перечисления в строку. Это продемонстрировано ниже:
enum Tristate {
False,
True,
Unknown
}
console.log(Tristate[0]); // "False"
console.log(Tristate["False"]); // 0
console.log(Tristate[Tristate.False]); // "False" потому что `Tristate.False == 0`
По умолчанию перечисления начинаются с 0
, а затем каждое последующее значение автоматически увеличивается на 1. В качестве примера рассмотрим следующее:
enum Color {
Red, // 0
Green, // 1
Blue // 2
}
Однако вы можете изменить число, связанное с любым элементом перечисления, назначив его конкретным. Это показано ниже, где мы начинаем с 3 и начинаем увеличивать:
enum Color {
DarkRed = 3, // 3
DarkGreen, // 4
DarkBlue // 5
}
СОВЕТ: Я обычно инициализирую первое перечисление с
= 1
, чтобы можно было наверняка проверить значение перечисления на достоверность.
Одним из отличных вариантов использования перечислений является возможность использовать перечисления в качестве Флагов
. Флаги позволяют вам проверить, верно ли определенное условие из набора условий. Рассмотрим следующий пример, где у нас есть набор свойств о животных:
enum AnimalFlags {
None = 0,
HasClaws = 1 << 0,
CanFly = 1 << 1,
EatsFish = 1 << 2,
Endangered = 1 << 3
}
Здесь мы используем оператор сдвига влево для перемещения 1
вокруг определенного уровня битов, чтобы получить побитовые непересекающиеся числа 0001
, 0010
, 0100
и 1000
(это десятичные числа 1
,2
,4
,8
если вам интересно). Побитовые операторы |
(или) / &
(и) / ~
(не) являются вашими лучшими друзьями при работе с флагами и продемонстрированы ниже:
enum AnimalFlags {
None = 0,
HasClaws = 1 << 0,
CanFly = 1 << 1,
}
type Animal = {
flags: AnimalFlags
}
function printAnimalAbilities(animal: Animal) {
var animalFlags = animal.flags;
if (animalFlags & AnimalFlags.HasClaws) {
console.log('животное имеет когти');
}
if (animalFlags & AnimalFlags.CanFly) {
console.log('животное может летать');
}
if (animalFlags == AnimalFlags.None) {
console.log('ничего');
}
}
let animal: Animal = { flags: AnimalFlags.None };
printAnimalAbilities(animal); // ничего
animal.flags |= AnimalFlags.HasClaws;
printAnimalAbilities(animal); // животное имеет когти
animal.flags &= ~AnimalFlags.HasClaws;
printAnimalAbilities(animal); // ничего
animal.flags |= AnimalFlags.HasClaws | AnimalFlags.CanFly;
printAnimalAbilities(animal); // животное имеет когти, животное может летать
Здесь:
- мы использовали
|=
, чтобы добавить флаги - комбинация
&=
и~
для очистки флага |
объединить флаги
Примечание: вы можете комбинировать флаги для создания удобных сокращений в определении перечисления, например
EndangeredFlyingClawedFishEating
ниже:
enum AnimalFlags {
None = 0,
HasClaws = 1 << 0,
CanFly = 1 << 1,
EatsFish = 1 << 2,
Endangered = 1 << 3,
EndangeredFlyingClawedFishEating = HasClaws | CanFly | EatsFish | Endangered,
}
Мы смотрели только на перечисления, где значениями являются числа
. На самом деле вы также можете использовать перечисления со строковыми значениями. Например
export enum EvidenceTypeEnum {
UNKNOWN = '',
PASSPORT_VISA = 'passport_visa',
PASSPORT = 'passport',
SIGHTED_STUDENT_CARD = 'sighted_tertiary_edu_id',
SIGHTED_KEYPASS_CARD = 'sighted_keypass_card',
SIGHTED_PROOF_OF_AGE_CARD = 'sighted_proof_of_age_card',
}
С ними проще работать и проверять, так как они предоставляют выразительные / проверяемые строковые значения.
Вы можете использовать эти значения для простого сравнения строк. Например
// Где `someStringFromBackend` будет '' | 'passport_visa' | 'passport' ... и т.д.
const value = someStringFromBackend as EvidenceTypeEnum;
// Пример использования в коде
if (value === EvidenceTypeEnum.PASSPORT){
console.log('Вы предоставили паспорт');
console.log(value); // `passport`
}
Если у вас есть определение перечисления, подобное следующему:
enum Tristate {
False,
True,
Unknown
}
var lie = Tristate.False;
Строка var lie = Tristate.False
компилируется в JavaScript как var lie = Tristate.False
(да, вывод совпадает с вводом). Это означает, что во время выполнения потребуется поиск Tristate
, а затем Tristate.False
. Чтобы повысить производительность, вы можете записать enum
как const enum
. Это продемонстрировано ниже:
const enum Tristate {
False,
True,
Unknown
}
var lie = Tristate.False;
генерирует следующий JavaScript:
var lie = 0;
то есть компилятор:
- Встраивает любое использование перечисления (
0
вместоTristate.False
). - Не генерирует JavaScript для определения перечисления (во время выполнения отсутствует переменная
Tristate
), поскольку его использование встроено.
Встраивание имеет очевидные преимущества в производительности. Тот факт, что во время выполнения отсутствует переменная Tristate
, просто означает что компилятор помогает вам не генерировать JavaScript, который фактически не используется во время выполнения. Однако вы, возможно, захотите, чтобы компилятор по-прежнему генерировал JavaScript-версию определения перечисления для перевода числа в строку или строки в число, как мы рассматривали выше. В этом случае вы можете использовать флаг компилятора --preserveConstEnums
, и он все равно сгенерирует определение var Tristate
, чтобы вы могли использовать Tristate["False"]
или Tristate[0]
вручную во время выполнения, если это нужно. Это никак не влияет на встраивание.
Вы можете использовать объявление перечисления
+ область имен
, чтобы добавить статические методы в перечисление. Следующий пример демонстрирует как мы добавляем статический элемент isBusinessDay
в перечисление Weekday
:
enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
namespace Weekday {
export function isBusinessDay(day: Weekday) {
switch (day) {
case Weekday.Saturday:
case Weekday.Sunday:
return false;
default:
return true;
}
}
}
const mon = Weekday.Monday;
const sun = Weekday.Sunday;
console.log(Weekday.isBusinessDay(mon)); // true
console.log(Weekday.isBusinessDay(sun)); // false
ПРИМЕЧАНИЕ: расширяемые перечисления имеют значение только в том случае, если вы не используете модули. Вы должны использовать модули. Следовательно, этот раздел является последним.
Вот сгенерированный JavaScript для перечисления из известного выше примера:
var Tristate;
(function (Tristate) {
Tristate[Tristate["False"] = 0] = "False";
Tristate[Tristate["True"] = 1] = "True";
Tristate[Tristate["Unknown"] = 2] = "Unknown";
})(Tristate || (Tristate = {}));
Мы уже объяснили часть Tristate[Tristate["False"] = 0] = "False";
. Теперь обратите внимание на оборачивающий код (function (Tristate) { /*code here */ })(Tristate || (Tristate = {}));
особенно часть (Tristate || (Tristate = {}));
. Он в основном описывает локальную переменную Tristate
, которая будет либо указывать на уже определенное значение Tristate
, либо инициализировать новый пустой объект {}
.
Это означает, что вы можете разделить (и расширить) определение перечисления на несколько файлов. Например, ниже мы разделили определение для Color
на два блока
enum Color {
Red,
Green,
Blue
}
enum Color {
DarkRed = 3,
DarkGreen,
DarkBlue
}
Обратите внимание, что вы должны повторно инициализировать первый элемент (здесь DarkRed = 3
) в продолжении перечисления, чтобы получить производный код, а не значения перезаписанные из предыдущего определения (то есть 0
, 1
, ... и так далее). TypeScript предупредит вас, если вы этого не сделаете (сообщение об ошибке В перечислении с несколькими объявлениями только одно объявление может не использовать инициализатор для своего первого элемента
).