ENUMs
Last updated
Last updated
ENUM is a set of values. This is something between a real object and a type. It is very important to understand that the Types and all TypeScript syntax are cut out after compilation from all resulting files. After compiling our code from TypeScript to JavaScript, we can open the generated file and we will see that there is no information about any TypeScript logic that we have been writing so diligently. The browser simply does not know how to work with any TypeScript-specific syntax. But ENUM is very special, it is something in between, i.e. somewhere on the verge - it seems to be both a type and an object.
Numeric ENUMs
If you would write the code for ENUM in any Integrated Development Environment (IDE) that supports TypeScript, you will see that your IDE automatically enumerates the values of ENUM in ascending order. As a result, you can access the values of ENUM that have been created (s, m, l, xl) or the numeric corresponding values that TS set for us. Thus, ENUMs are reversible.
In fact, ENUM is an object whose properties will be the ones that we set, and the property values are the values that TS will set automatically (in the case we did not explicitly set them). When we access a property, the value will be returned:
It is important to understand that ENUM is not only a type, but it is also an object.
In TypeScript Playground you can see our TS code on the left side of the screen, and the JS code that we get after compilation. We can see that after compilation we get all these Number values that have been assigned in ascending order to every single property of our ENUM-object.
But if we would explicitly assign values, then the default numeration will shift, and each subsequent property value will be calculated by incrementing the previous value.
Automatic incrementing of property values occurs only with numeric values. This reverse mapping nature of numeric ENUMs is an important property which means that we can access the internals of ENUMs both through a property and through a property value (whether it is a default property value or an explicitly assigned one).
2. String ENUMs
String ENUMs are ENUMs whose values are strings.
If we set at least one property value to a string value and leave subsequent properties without a value at all, then there will be an error - Enum member must have initializer. If the ENUM element has no value, then it looks at the value of the previous one. If the previous property value is a number, then the value will simply be incremented (+1). But if that value is a string, then there will be an error and we will need to manually write the values.
Thus, if at least one property value is set for the string ENUM, all other properties have to be manually set as well.
Mixed ENUM
If some of the property values of ENUM were strings, and we specify a numeric value for one of the elements in Mixed ENUM, then all subsequent property values will be numbered using the increment (+1) again.
In the images above we can see that the first 2 property values are strings, we set them up, and the 3rd one is numeric, so TypeScript can help us to assign all subsequent values by incrementation as it always does for all numeric ENUMs.
Thus, we can conclude:
only numeric ENUMs have reversibility (reverse mapping) and auto-computation of properties;
string ENUMs do not have reversibility and auto-computation of properties;
How the property value's auto-computing for numeric ENUM works:
The value of the previous property is checked, if it is a number, then the current value is incremented by +1 and assigned to the current property of the object;
If the previous value is a string, then TS requires you to fill in the current value manually;
In the picture from TS Playground above, we can see that after code compilation, an immediately called function (IIFE - Immediately Invoked Function Expression) is created.
Но при смешанном типе, если мы вновь укажем числовое значение одному из элементов, то вновь будут вычислены/разнумерованы с помощью инкремента +1 все последующие нижестоящие значения свойств.На картинке из TS playground мы также видим в правой части, что
реверсивностью и автовычислением свойств обладает только числовые ENUM;
строковые ENUM реверсивностью и автовычислением свойств не обладают;
Работа автовычисления значения свойств у ENUM:
Берется значение предыдущего свойства, если это число, то текущее значение инкрементируется +1 и присваивается;
Если предыдущее значение является строкой, то TS требует заполнить текущее значение вручную;
На картинке выше мы также видим, что после компиляции создается немедленно вызываемая функция (IIFE - Immediately Invoked Function Expression).
3. Смешанные ENUM
ENUM могут быть смешанными, но это получается сборная солянка ближе к any. Лучше это никогда не использовать. Но в целом использование ENUM переоценено.
4. Константные ENUM
При добавлении ключевого слова const перед объявлением ENUM, в Playground мы увидим пустоту после компиляции, потому что константные enum используются только для типизации или как набор констант, т.е. никакого объекта не создается(!). Если мы обратимся к свойству, то увидим, что использование ENUM у нас вырезалось и заменилось константными значениями - обычными литералами.
Зачем придумали константные ENUM?
Если мы уберем const, то мы увидим что обычный ENUM несет с собой кучу кода. Если у нас на проекте сотни ENUMs в проекте, то это огромное количество объектов. И если мы их используем только для типизации, или для того чтобы там константы хранить, то нет смысла постоянно создавать ENUM классическим образом. Лучше тогда использовать константный ENUM, который не попадет в JS и наш bundle будет легче. Но у константных ENUMs есть одно ограничение - т.к. реальный объект не создается, то и работать с таким ENUM как с объектом нельзя. Например, нельзя использовать Object.keys()/Object.values()/Object.entries(), etc, т.к. это не объект, а при обычном ENUM можно использовать все методы объекта. По сути все что мы можем сделать с константным ENUM - это обратиться к его свойствам.
Итого: если мы решили использовать ENUM, нужно задать вопрос - Зачем?
Если просто для типизации или просто как константы - то нужно использовать const enum;
Если мы хотим использовать ENUM как полноценный объект, мы хотим итерироваться по нему, использовать другие методы объекта, то нужно использовать классический ENUM, без const.
Проблемы ENUM:
Ввиду того, что понятно тип это или реальный объект, возникают проблемы при работе с ENUM, при работе со строками. Н-р: с GraphQL работают иногда некорректно, и раньше была проблема с Babel - раньше Babel не понимал, как их поддерживать - объекты это или типы.
Также ENUM занимают много место, потому что они добавляют много доп. кода, и когда их сотни, то это может повлиять на размер бандла. Но стоит помнить, что облегчение бандла дает прирост в скорости только на определенных этапах - зависимость не прямая. Приходит момент, когда мы уменьшаем бандл, а никакого результата в скорости уже нет.
СПОСОБЫ НЕ ИСПОЛЬЗОВАТЬ ENUM
Literal Types (были разобраны выше):
let fontWeight: 500 | 600 | 700 | "bold" = "bold";Когда мы присвоим "bold" этой переменной, все типы вырежуться, т.е. никаких последствий и никакого доп.кода не будет создаваться. Типы будут лишь до компиляции. В отличие от ENUM, который даже после компиляции TS в JS принесет с собой много нового ненужного кода.
использование as const
Если нам нужен ENUM, и мы хотим работать с ним как с объектом, то константный ENUM нам не подойдет (т.к. методы объекта к нему не применимы).Да, в отличие от ENUM этот метод не дает нам автозаполнения значений, но в большинстве случаев, будем честны, нумерованное автозаполнение нам не нужно. Почти всегда ENUM это набор свойств со строковыми значениями. Если мы объявим объект со свойствами - то это будет просто объект, но если мы добавим к нему as const, то объект станет зафриженным, т.е. все его свойства будут защищены (readonly), т.е. их нельзя изменить. В этот объект ничего нельзя будет добавить или изменить - никаких новых свойств, и никакого изменения свойств существующих. Итого с помощью as const мы получили некий набор свойств, т.е. перечисление. Для типизации их никак не использовать, но это набор настоящих констант. Когда нам нужны просто типы, то нужно использовать literal types. Для as const не создается IIFE function как для ENUM, это простой классический объект. Также при сравнении строк, при присвоении строк у ENUM иногда могут быть проблемы из-за того что это не просто объект, а еще и тип. А в случае с обычным объектом - нет никаких ограничений, это обычный объект.