Привет Всем.
Меня зовут Михаил. В НЛМК нахожусь на должности Frontend-разработчика. Занимаюсь разработкой внутренних информационных систем на React + Typescript.
В этой статье поговорим про тип enum (перечисление) в Typescript, о случаях когда его можно и нужно использовать, а когда нет.
Перечисления бывают числовыми и строковыми. Например, представим в виде перечисления должности работников кафе.
Рассмотрим перечисления с числовыми значениями:
![](https://habrastorage.org/getpro/habr/upload_files/ed8/78f/ebf/ed878febf943acaf3afc4327e0099b2c.png)
Каждому элементу перечисления соответствует числовая константа начиная с 0 и увеличивается на 1 при движении сверху вниз по перечислению.
В этом легко убедиться:
![](https://habrastorage.org/getpro/habr/upload_files/256/e01/ac4/256e01ac48aca189dcf9bdbc2cc4ff0f.png)
в консоле мы увидим следующее:
![](https://habrastorage.org/getpro/habr/upload_files/925/e82/907/925e82907aef60152986fcde96b195ea.png)
Можно менять порядок, если проинициализировать один из элементов числовым значением.
![](https://habrastorage.org/getpro/habr/upload_files/dd9/875/b67/dd9875b671dfda6845ebf753a90cd869.png)
Тогда элементы после будут увеличены на 1 от заданного значения:
![](https://habrastorage.org/getpro/habr/upload_files/0e2/dce/b35/0e2dceb35e4481b689cfec49a9d86d09.png)
Рассмотрим перечисления со строковыми значениями:
![](https://habrastorage.org/getpro/habr/upload_files/da9/7a7/355/da97a7355ef50e14792fa4383c04ba42.png)
в консоли мы увидим:
![](https://habrastorage.org/getpro/habr/upload_files/6bf/0ac/320/6bf0ac3207909d9e1f8b34a7f1116964.png)
Числовые и строковые перечисления можно смешивать.
TypeScript компилируется в JavaScript. При компиляции типы удаляются и остается чистый JavaScript код. Например, простая функция суммирования с определением типов
![](https://habrastorage.org/getpro/habr/upload_files/1df/438/02a/1df43802a99c8a8c78bae06000443224.png)
превратится в:
![](https://habrastorage.org/getpro/habr/upload_files/e3d/61c/44f/e3d61c44f3ba541f17cae0ded22dee11.png)
Как мы видим, компилятор удалил все объявления типов.
Но с enum это работает немного иначе.
Разберем для чего и когда можно использовать enum, а когда лучше обойтись простым константным объектом.
Основной аргумент против enum - при компиляции enum, компилятор создает дополнительный JavaScript код и усложняет работу компилятору.
Вернемся к примеру с перечислением должностей работников кафе.
Используем enum и посмотрим на результат компиляции:
![](https://habrastorage.org/getpro/habr/upload_files/7b1/9a4/b24/7b19a4b24be3603794ff62362a341b02.png)
превратилось в:
![](https://habrastorage.org/getpro/habr/upload_files/ec8/6d7/629/ec86d7629d12718c78b7f212b057666f.png)
Получили не очень приятную конструкцию, состоящую из переменной и анонимного самовызывающегося функционального выражения.
При этом можно использовать методы объекта, например:
![](https://habrastorage.org/getpro/habr/upload_files/356/88e/44a/35688e44a65b25a1b37087ddbc1a8f6c.png)
В консоли мы увидим:
![](https://habrastorage.org/getpro/habr/upload_files/fab/860/89f/fab86089fdb5204bbf6939bc3eb0a923.png)
Попробуем заменить enum на константный объект и посмотрим результаты компиляции:
![](https://habrastorage.org/getpro/habr/upload_files/943/dd4/fab/943dd4fab0a0b0c6a5a154ad84124a0f.png)
превратится в:
![](https://habrastorage.org/getpro/habr/upload_files/96a/1ee/bc3/96a1eebc3d3bbcf736488f75d86fe5ce.png)
Тут константный объект определенно хорош.
Но enum может еще лучше!
Для этого используем ключевое слово const:
![](https://habrastorage.org/getpro/habr/upload_files/056/58a/3a0/05658a3a06d9ef1c072d8aff64a8d9a5.png)
Взглянем на результат компиляции:
![](https://habrastorage.org/getpro/habr/upload_files/8de/ab2/a95/8deab2a95103ad478143d4159b420bf0.png)
Теперь нет объекта, нет лишней переменной и анонимного самовызывающегося функционального выражения.
Получается, enum использовать даже профитнее, чем объект?
В этой части да, но есть несколько нюансов:
-
Если в вашем проекте в файле tsconfig.json в секции compilerOptions свойство preserveConstEnums выставлено со значением true, то эффекта от использования const enum не будет.
Проверим это на нашем примере.
Поставим флаг в true:
![](https://habrastorage.org/getpro/habr/upload_files/e47/c01/3fc/e47c013fc3ed47565b129b2306ae69a7.png)
И посмотрим на результат компиляции:
![](https://habrastorage.org/getpro/habr/upload_files/97d/1f7/07d/97d1f707d6255a53a0eaa7680fd606c0.png)
превратился в:
![](https://habrastorage.org/getpro/habr/upload_files/cc4/fcd/1c8/cc4fcd1c827be68a07209c4533f37b63.png)
Т.е. весь эффект от использования const enum потерян и не имеет смысла.
Если вам необходим только доступ к значению, то однозначно используйте enum.
А вот если вам нужно где-то перебрать или получить все значения или ключи, то используйте константный объект а не const enum.
Почему не const enum, смотрим:
![](https://habrastorage.org/getpro/habr/upload_files/c05/6d2/654/c056d265409b7fdb0a077c0e6e26c973.png)
Мы увидим ошибку:
![](https://habrastorage.org/getpro/habr/upload_files/5ac/7d3/10b/5ac7d310b094bf51122b807141eb4f82.png)
А все потому, что нашего const enum не окажется в скомпилированном коде.
Когда необходимо получить все значения или ключи, константный объект в скомпилированном коде сильно выигрывает у enum без использования const.
О типизации
Типизируем параметр принимаемый функцией “printPosition”:
![](https://habrastorage.org/getpro/habr/upload_files/08b/e4c/cdd/08be4ccdd7cf40d2cafd74384102a5d2.png)
или так:
![](https://habrastorage.org/getpro/habr/upload_files/b24/bbb/4d2/b24bbb4d2bfc7d2a522909fe5c9af4dd.png)
Но руками создавать union тип - это не то, что хотелось бы делать.
При использовании enum:
если нам где-то понадобится типизировать принимаемый параметр по ключам enum.
![](https://habrastorage.org/getpro/habr/upload_files/bb5/c4c/5b5/bb5c4c5b52a1fa6b26b97666bd2512d3.png)
если нам где-то понадобится типизировать принимаемый параметр по значениям enum:
![](https://habrastorage.org/getpro/habr/upload_files/ed3/700/957/ed37009576fb3d87657d9f7f017bd51b.png)
достаточно использовать шаблонную строку. Главное не забывать использовать const.
Итак, подытожим плюсы и минусы использования enum:
Минусы:
При использовании enum без const компилятор создает дополнительный JavaScript код, что усложняет работу компилятору и размер финального бандла;
При использовании enum с const - нет возможности получить для обработки все ключи или значения enum.
Необходимо следить за флагом свойства preserveConstEnums в файле tsconfig.json в секции compilerOptions, чтобы получить профит.
Плюсы:
При использовании const enum и необходимости только в значении дает отсутствие избыточного кода в скомпилированном JavaScript, а значит, меньший размер и лучшую производительность;
При использовании const enum - возможность быстрой и удобной типизации в отличие от использования константных объектов;
Использование enum упрощает рефакторинг кода, изменение значения enum автоматически обновляет все его использования в проекте. Это помогает избежать ошибок, связанных с поиском и заменой значений вручную.
Обнаружение ошибок на этапе компиляции: TypeScript выдаст ошибку на этапе компиляции, если использовать значение, которое не входит в enum. Это помогает избежать ошибок при использовании неверных значений.
Автодополнение в редакторах кода: Редакторы кода предлагают автодополнение для значений enum, что ускоряет процесс написания кода, уменьшает вероятность опечаток и ошибок.
Самодокументируемый код: Определение enum с именованными значениями добавляет дополнительную информацию о том, какие значения ожидаются и задумывались разработчиком. Это делает код более самодокументируемым и легче понимаемым для других разработчиков.
Итого:
На мой взгляд, понимание плюсов и минусов использования enum при разработке несомненно даст исключительно положительный эффект, как минимум, это уменьшение размера бандла и легкая типизация.
Спасибо за внимание! Пишите комментарии, буду рад узнать ваше мнение по данному вопросу!
Комментарии (2)
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
Andchir
На практике получалось ли получить ощутимую разницу? У вас сотни енамов коде одного проекта?
На сколько эта разница в производительности существенна не хотите проверить?
Логично. Именно поэтому и существуют эти два типа данных в языке.