---
title: "Почему я всегда использую enum в JavaScript"
description: "Почему именованные значения и поиск по символам надёжнее строковых литералов в поддерживаемом JavaScript-коде."
date: 2024-05-23
---

Эта заметка появилась под впечатлением от статьи «Why I don't use enums
in TypeScript, and neither should you», которая сейчас удалена. Её автор
предлагал вместо `enum` использовать в TypeScript объекты.

<zoom-img src="/medium/img/2feb64992e54/image-01.png" alt="Сравнение enum и объекта с константами" width="100%"></zoom-img>

То есть вместо:

``` ts
enum Colors {
  BLUE = 'blue',
  GREEN = 'green',
  RED = 'red',
}
function printColor(color: Colors) { console.log(color); }
printColor(Colors.BLUE);
printColor('blue');
```

предлагалось писать:

``` ts
const Colors = {
  BLUE: 'blue',
  GREEN: 'green',
  RED: 'red',
} as const;
type Colors = typeof Colors[keyof typeof Colors];
function printColor(color: Colors) { console.log(color); }
printColor(Colors.BLUE);
printColor('blue');
```

Преимуществом называли возможность писать литерал `'blue'`, не
импортируя модуль с перечислением.

В статье [«Какова конечная цель
программирования?»](https://flancer32.com/so-what-is-the-ultimate-goal-of-programming-0fd5def59fdc)
я рассматриваю иерархию целей разработки, похожую на пирамиду Маслоу.

<zoom-img src="/medium/img/2feb64992e54/image-02.png" alt="Иерархия целей программирования" width="100%"></zoom-img>

Подход со строковыми литералами относится к первому уровню — удобству
написания кода. Но он не достигает высшего уровня, который я называю
*изменяемостью*.

Действительно, литерал писать проще:

``` js
invoice.setState('pending');
order.setState('pending');
```

Но при поиске всех состояний `pending` для заказов они смешаются с
такими же состояниями счетов. Именно от этого защищают кажущиеся
неудобствами импорт и отказ от литералов.

В [моём
JS-коде](https://github.com/teqfw/core/blob/1af0e85783e95fbbbdee82a8b116eb17e9bc34a5/src/Shared/Enum/Sphere.mjs)
я использую пространства имён, унаследованные от Zend1:

``` js
const TeqFw_Core_Shared_Enum_Sphere = {
  BACK: 'BACK',
  FRONT: 'FRONT',
  SHARED: 'SHARED',
};
Object.freeze(TeqFw_Core_Shared_Enum_Sphere);
export default TeqFw_Core_Shared_Enum_Sphere;
```

При использовании подключаю перечисление через [внедрение
зависимостей](https://github.com/teqfw/web/blob/441d166c8cea10cbc70f649bcca6df008c24c431/src/Back/App/Server/Handler/Config/A/Di.mjs#L30)
и
[JSDoc](https://github.com/teqfw/web/blob/441d166c8cea10cbc70f649bcca6df008c24c431/src/Back/App/Server/Handler/Config/A/Di.mjs#L21):

``` js
export default function ({ TeqFw_Core_Shared_Enum_Sphere$: SPHERE }) {
  if (one.sphere === SPHERE.FRONT || one.sphere === SPHERE.SHARED) {
    // ...
  }
}
```

Так обычным текстовым поиском находятся все места использования
`TeqFw_Core_Shared_Enum_Sphere`. JSDoc помогает IDE перейти к каждому
значению — `BACK`, `FRONT` или `SHARED`.

JavaScript, в отличие от TypeScript, не запрещает передать литерал
напрямую. Но ответственность разработчика — использовать возможности
инструментов во благо, а не во вред. Да, `enum` в JavaScript фактически
не реализован, хотя слово зарезервировано. Но я считаю полезным писать
код так, как будто он есть.
