Typescript: Union Type vs Enum
-
Часто объединенные типы
Union Type
и перечисляемый типEnum
- это онди из способов обеспечения типобезопасности кода, в частности типизация ограниченного списка каких либо значений.Они также оба хорошо работают с современными IDE предлагая в подсказках отфильтрованные значения.
Но бывает задаешься вопросом: что лучше использовать в той или иной ситуации?
Разница в размере кода
Объединенные типы не будут создавать какой либо дополнительный код.
Вот Простой пример:Версия с Union Type
type VehicleTypeUnion = 'SUV' | 'SEDAN' | 'TRUCK'; const a: VehicleTypeUnion = 'SUV'
->
"use strict"; const a = 'SUV';
Версия с Enum
enum VehicleTypeEnum { SUV, SEDAN, TRUCK, } const b: VehicleTypeEnum = VehicleTypeEnum.SUV
->
var VehicleTypeEnum; (function (VehicleTypeEnum) { VehicleTypeEnum[VehicleTypeEnum["SUV"] = 0] = "SUV"; VehicleTypeEnum[VehicleTypeEnum["SEDAN"] = 1] = "SEDAN"; VehicleTypeEnum[VehicleTypeEnum["TRUCK"] = 2] = "TRUCK"; })(VehicleTypeEnum || (VehicleTypeEnum = {})); const b = VehicleTypeEnum.SUV;
Разные типы в списке значений
перечисляемый тип обычно описывается в виде числовых значений или в виде строковых.
// список enum VehicleType1 { SUV, SEDAN, TRUCK, BUS, MOTORCYCLE } // эквивалентный список enum VehicleType2{ SUV = 0, SEDAN = 1, TRUCK = 2, BUS = 3, MOTORCYCLE = 4 } // список со строковыми значениями enum VehicleType3{ SUV = 'SUV', SEDAN = 'SEDAN', TRUCK = 'TRUCK', BUS = 'BUS', MOTORCYCLE = 'MOTORCYCLE' }
->
"use strict"; // перечисляемый тип var VehicleType1; (function (VehicleType1) { VehicleType1[VehicleType1["SUV"] = 0] = "SUV"; VehicleType1[VehicleType1["SEDAN"] = 1] = "SEDAN"; VehicleType1[VehicleType1["TRUCK"] = 2] = "TRUCK"; VehicleType1[VehicleType1["BUS"] = 3] = "BUS"; VehicleType1[VehicleType1["MOTORCYCLE"] = 4] = "MOTORCYCLE"; })(VehicleType1 || (VehicleType1 = {})); // эквивалентный тип var VehicleType2; (function (VehicleType2) { VehicleType2[VehicleType2["SUV"] = 0] = "SUV"; VehicleType2[VehicleType2["SEDAN"] = 1] = "SEDAN"; VehicleType2[VehicleType2["TRUCK"] = 2] = "TRUCK"; VehicleType2[VehicleType2["BUS"] = 3] = "BUS"; VehicleType2[VehicleType2["MOTORCYCLE"] = 4] = "MOTORCYCLE"; })(VehicleType2 || (VehicleType2 = {})); // тип со строковыми значениями var VehicleType3; (function (VehicleType3) { VehicleType3["SUV"] = "SUV"; VehicleType3["SEDAN"] = "SEDAN"; VehicleType3["TRUCK"] = "TRUCK"; VehicleType3["BUS"] = "BUS"; VehicleType3["MOTORCYCLE"] = "MOTORCYCLE"; })(VehicleType3 || (VehicleType3 = {}));
Первый тип мы описали без инициализирующих значений, его значения инициализируются автоматически, в зависимости от порядка, поэтому порядок значений менять опасно!
Это также накладывает ограничение, что мы не можем использовать тип с проинициализированными значениями и без:
enum VehicleType { SUV = 'SUV', SEDAN = 'SEDAN', TRUCK = 'TRUCK', BUS = 'BUS', MOTORCYCLE }
Выведет ошибку
error TS1061: Enum member must have initializer.
Вам придется описать тип так:
enum VehicleType { SUV = 'SUV', SEDAN = 'SEDAN', TRUCK = 'TRUCK', BUS = 'BUS', MOTORCYCLE = 0, }
Union Type
Тут вы можете создавать объединения с другими типами значений, отличными от строк:
type VehicleType = 'SUV' | 'SEDAN' | 'TRUCK' | 0 | [];
Это дает вам дополнительную гибкость в определении типов. Однако такая дополнительная гибкость может привести к неожиданному поведению в коде при отсутствии четкого понимания типа переменной, которая может быть строкой, или числом, или массивом объектов и т.д.
Перебор значений
Перечисления - это объекты, где каждому ключу свойства может быть автоматически присвоено значение или строка на случай, если мы определим значение вручную. Следовательно, вы можете повторять значения свойств точно так же, как и любой другой объект:
export enum VehicleType { SUV = 'SUV', SEDAN = 'SEDAN', TRUCK = 'TRUCK', BUS = 'BUS', MOTORCYCLE = 'MOTORCYCLE' } for (const type in VehicleType) { console.log(type); } Object.keys(VehicleType).forEach((key) => { console.log('key ', key); })
Попытка сделать что-то подобное с union types приведет к ошибке при попытке выполнения кода
type VehicleType = 'SUV' | 'SEDAN' | 'TRUCK' | 'BUS' | 'MOTORCYCLE'; for (const type in VehicleType) { // my logic in here console.log(type); } Object.keys(VehicleType).forEach((key) => { console.log('key ', key); })
error TS2693: 'VehicleType' only refers to a type, but is being used as a value here.
Вывод
Если вам нужно будет выполнять итерации по набору значений, лучше всего использовать
enums
, но вы тем самым пожертвуете размером собранного кода.