Привет Всем.

Меня зовут Михаил. В НЛМК нахожусь на должности Frontend-разработчика.  Занимаюсь разработкой внутренних информационных систем на React + Typescript.

В этой статье поговорим про тип enum (перечисление) в Typescript, о случаях когда его можно и нужно использовать, а когда нет.

Перечисления бывают числовыми и строковыми. Например, представим в виде перечисления должности работников кафе.

Рассмотрим перечисления с числовыми значениями:

Каждому элементу перечисления соответствует числовая константа начиная с 0 и увеличивается на 1 при движении сверху вниз по перечислению.

В этом легко убедиться:

в консоле мы увидим следующее:

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

Тогда элементы после будут увеличены на 1 от заданного значения:

Рассмотрим перечисления со строковыми значениями:

в консоли мы увидим:

Числовые и строковые перечисления можно смешивать.

TypeScript компилируется в JavaScript. При компиляции типы удаляются и остается чистый JavaScript код. Например, простая функция суммирования с определением типов

превратится в:

Как мы видим, компилятор удалил все объявления типов.

Но с enum это работает немного иначе.

Разберем для чего и когда можно использовать enum, а когда лучше обойтись простым константным объектом.

Основной аргумент против enum - при компиляции enum, компилятор создает дополнительный JavaScript код и усложняет работу компилятору.

Вернемся к примеру с перечислением должностей работников кафе.

Используем enum и посмотрим на результат компиляции: 

превратилось в:

Получили не очень приятную конструкцию, состоящую из переменной и анонимного самовызывающегося функционального выражения.

При этом можно использовать методы объекта, например:

В консоли мы увидим: 

Попробуем заменить enum на константный объект и посмотрим результаты компиляции:

превратится в: 

Тут константный объект определенно хорош.

Но enum может еще лучше! 

Для этого используем ключевое слово const:

Взглянем на результат компиляции:

Теперь нет объекта, нет лишней переменной и анонимного самовызывающегося функционального выражения.

Получается, enum использовать даже профитнее, чем объект?

В этой части да, но есть несколько нюансов:

  1. Если в вашем проекте в файле tsconfig.json в секции compilerOptions  свойство  preserveConstEnums выставлено со значением  true, то эффекта от использования const enum не будет.

Проверим это на нашем примере.

Поставим флаг в true:

И посмотрим на результат компиляции:

превратился в: 

Т.е. весь эффект от использования const enum потерян и не имеет смысла.

  1. Если вам необходим только доступ к значению,  то однозначно используйте enum.

А вот если вам нужно где-то перебрать или получить все значения или ключи, то используйте константный объект а не const enum.

Почему не const enum, смотрим:

Мы увидим ошибку:

А все потому, что нашего const enum не окажется в скомпилированном коде.

Когда необходимо получить все значения или ключи, константный объект в скомпилированном коде сильно выигрывает у enum без использования const.

О типизации

Типизируем параметр принимаемый функцией “printPosition”:

или так:

Но руками создавать union тип - это не то, что хотелось бы делать.

При использовании enum

  • если нам где-то понадобится типизировать принимаемый параметр по ключам enum.

  • если нам где-то понадобится типизировать принимаемый параметр по значениям enum:

достаточно использовать шаблонную строку. Главное не забывать использовать const

Итак, подытожим плюсы и минусы использования enum:

Минусы:

  1. При использовании enum без const компилятор создает дополнительный JavaScript код, что усложняет работу компилятору и размер финального бандла; 

  2. При использовании enum с const - нет возможности получить для обработки все ключи или значения enum.

  3. Необходимо следить за флагом свойства preserveConstEnums в файле  tsconfig.json в секции compilerOptions, чтобы получить профит.  

Плюсы:

  1. При использовании const enum и необходимости только в значении дает отсутствие избыточного кода в скомпилированном  JavaScript, а значит, меньший размер и лучшую производительность;

  2. При использовании const enum - возможность быстрой и удобной типизации в отличие от использования константных объектов;

  3. Использование enum упрощает рефакторинг кода, изменение значения enum автоматически обновляет все его использования в проекте. Это помогает избежать ошибок, связанных с поиском и заменой значений вручную.

  4. Обнаружение ошибок на этапе компиляции: TypeScript выдаст ошибку на этапе компиляции, если использовать значение, которое не входит в enum. Это помогает избежать ошибок при использовании неверных значений.

  5. Автодополнение в редакторах кода: Редакторы кода предлагают автодополнение для значений enum, что ускоряет процесс написания кода, уменьшает вероятность опечаток и ошибок.

  6. Самодокументируемый код: Определение enum с именованными значениями добавляет дополнительную информацию о том, какие значения ожидаются и задумывались разработчиком. Это делает код более самодокументируемым и легче понимаемым для других разработчиков.

Итого:

На мой взгляд, понимание плюсов и минусов использования enum при разработке несомненно даст исключительно положительный эффект, как минимум, это уменьшение размера бандла и легкая типизация.

Спасибо за внимание! Пишите комментарии, буду рад узнать ваше мнение по данному вопросу!

Комментарии (2)


  1. Andchir
    28.12.2023 08:49
    +3

    При использовании enum без const компилятор создает дополнительный JavaScript код, что усложняет работу компилятору и размер финального бандла

    На практике получалось ли получить ощутимую разницу? У вас сотни енамов коде одного проекта?

    При использовании const enum и необходимости только в значении дает отсутствие избыточного кода в скомпилированном  JavaScript, а значит, меньший размер и лучшую производительность

    На сколько эта разница в производительности существенна не хотите проверить?

    А вот если вам нужно где-то перебрать или получить все значения или ключи, то используйте константный объект а не const enum.

    Логично. Именно поэтому и существуют эти два типа данных в языке.


  1. Onni
    28.12.2023 08:49

    Я использовал enum в CocosCreator так:

    enum UnitId { }
    enum ItemId { }
    
    var units: Recor<UnitId, Unit>;
    
    function spendItem(unitId: UnitId, itemId: ItemId, amount: Int) {
      ...
    }

    Вместо того что бы использовать простой number. Так компилятор не давал перепутать id-ки и и выполнить: `const unit = units[itemId]`

    Правда не нашел какими флагами это включается на TSPlayground