В TypeScript есть несколько различных способов определения перечисления:
enum Alpha { X, Y, Z }
const enum Beta { X, Y, Z }
declare enum Gamma { X, Y, Z }
declare const enum Delta { X, Y, Z }
Если я попытаюсь использовать значение из Gamma
во время выполнения, я получаю сообщение об ошибке, потому что Gamma
не определено, но это не относится к Delta
или Alpha
? Что const
или declare
значит на декларациях здесь?
Также есть preserveConstEnums
флаг компилятора - как он с ними взаимодействует?
enums
typescript
Райан Кавано
источник
источник
Ответы:
Вы должны знать четыре различных аспекта перечислений в TypeScript. Для начала несколько определений:
"объект поиска"
Если вы напишете это перечисление:
TypeScript выдаст следующий объект:
Я буду называть его поисковым объектом . Его цель двоякая: служить отображением строк в числа , например, при записи
Foo.X
илиFoo['X']
, и служить отображением чисел в строки . Это обратное сопоставление полезно для целей отладки или ведения журнала - вы часто будете иметь значение0
или1
и хотите получить соответствующую строку"X"
или"Y"
."объявить" или " окружающий "
В TypeScript вы можете «объявлять» вещи, о которых должен знать компилятор, но не генерировать код на самом деле. Это полезно, когда у вас есть библиотеки, такие как jQuery, которые определяют некоторый объект (например
$
), о котором вы хотите ввести информацию, но не нуждаетесь в каком-либо коде, созданном компилятором. Спецификация и другая документация ссылаются на сделанные таким образом объявления как на «окружающий» контекст; Важно отметить, что все объявления в.d.ts
файле являются «внешними» (либо требуют явногоdeclare
модификатора, либо имеют его неявно, в зависимости от типа объявления)."встраивание"
По соображениям производительности и размера кода часто предпочтительнее иметь ссылку на член перечисления, замененную его числовым эквивалентом при компиляции:
В спецификации эта подстановка называется , я назову ее встраиванием, потому что она звучит круче. Иногда вам не нужно, чтобы члены перечисления были встроены, например, потому что значение перечисления может измениться в будущей версии API.
Enums, как они работают?
Давайте разберем это по каждому аспекту перечисления. К сожалению, каждый из этих четырех разделов будет ссылаться на термины из всех остальных, поэтому вам, вероятно, придется прочитать все это целиком более одного раза.
вычисленное vs невычисленное (константа)
Члены перечисления могут быть вычислены или нет. Спецификация вызовов , не вычисляемые члены постоянной , но я буду называть их не вычисленный , чтобы избежать путаницы с сопзЬ .
Вычисляются член перечисления один, значение которого не известно во время компиляции. Ссылки на вычисляемые члены, конечно, не могут быть встроены. И наоборот, не-вычисленный членом является перечислением раз, значение которого будет известно во время компиляции. Ссылки на невычисляемые элементы всегда встраиваются.
Какие члены перечисления вычисляются, а какие не вычисляются? Во-первых, все члены
const
перечисления постоянны (т. Е. Не вычисляются), как следует из названия. Для неконстантного перечисления это зависит от того, смотрите ли вы на внешнее (объявленное) перечисление или на внешнее перечисление.Член
declare enum
(то есть окружающее перечисление) является константой тогда и только тогда, когда у него есть инициализатор. В противном случае он вычисляется. Обратите внимание, что в adeclare enum
разрешены только числовые инициализаторы. Пример:Наконец, элементы перечислений, не объявленных неконстантными, всегда считаются вычисленными. Однако их инициализирующие выражения сокращаются до констант, если они вычислимы во время компиляции. Это означает, что неконстантные члены перечисления никогда не встраиваются (это поведение изменилось в TypeScript 1.5, см. «Изменения в TypeScript» внизу)
константа против неконстантной
Const
Объявление перечисления может иметь
const
модификатор. Если перечисление естьconst
, все ссылки на его члены встроены.Перечисления const не создают объект поиска при компиляции. По этой причине ссылка
Foo
в приведенном выше коде является ошибкой, за исключением ссылки на член.Foo
Во время выполнения не будет никаких объектов.неконстантный
Если объявление перечисления не имеет
const
модификатора, ссылки на его элементы встроены, только если член не вычисляется. Неконстантное перечисление без объявления создаст объект поиска.объявить (окружающий) vs не объявить
Важное предисловие состоит в том, что
declare
в TypeScript есть очень конкретное значение: этот объект существует где-то еще . Это для описания существующих объектов. Использованиеdeclare
для определения объектов, которые на самом деле не существуют, может иметь плохие последствия; мы рассмотрим их позже.объявить
A
declare enum
не будет генерировать поисковый объект. Ссылки на его элементы встроены, если эти элементы вычисляются (см. Выше о вычисленных и невычисленных).Важно отметить , что другие формы ссылки на
declare enum
будут разрешены, например , этот код не ошибка компиляции , но будет не в состоянии во время выполнения:Эта ошибка относится к категории «Не лгите компилятору». Если у вас нет объекта, названного
Foo
во время выполнения, не пишитеdeclare enum Foo
!A
declare const enum
не отличается от aconst enum
, за исключением случая --preserveConstEnums (см. Ниже).не объявлять
Перечисление без объявления создает объект поиска, если это не так
const
. Встраивание описано выше.--preserveConstEnums флаг
Этот флаг имеет только один эффект: перечисления констант без объявления будут генерировать объект поиска. Встраивание не затрагивается. Это полезно для отладки.
Общие ошибки
Самая распространенная ошибка - использовать
declare enum
вместо обычногоenum
илиconst enum
более подходящего. Распространенная форма такова:Помните золотое правило: никогда
declare
то, чего на самом деле не существует . Используйте,const enum
если вы всегда хотите встраивать, илиenum
если вам нужен объект поиска.Изменения в TypeScript
Между TypeScript 1.4 и 1.5 было изменение в поведении (см. Https://github.com/Microsoft/TypeScript/issues/2183 ), чтобы все члены не объявленных неконстантных перечислений считались вычисленными, даже если они явно инициализируются литералом. Это, так сказать, «нерасщепляет ребенка», делая встраиваемое поведение более предсказуемым и более четко отделяя концепцию
const enum
от обычногоenum
. До этого изменения невычисляемые члены неконстантных перечислений встраивались более агрессивно.источник
enum
типов, спасибо!const
для объявленных типов перечислений.Здесь происходит несколько вещей. Давайте рассмотрим случай за случаем.
перечислить
Во-первых, простое старое перечисление. При компиляции в JavaScript будет создана таблица поиска.
Таблица поиска выглядит так:
Затем, когда у вас есть
Cheese.Brie
TypeScript, он излучаетCheese.Brie
в JavaScript, который оценивается как 0.Cheese[0]
излучаетCheese[0]
и фактически оценивает"Brie"
.константное перечисление
На самом деле никакого кода для этого не создается! Его значения встроены. Следующее излучает само значение 0 в JavaScript:
const enum
s 'встраивание может быть полезно по соображениям производительности.А как насчет
Bread[0]
? Это приведет к ошибке во время выполнения, и ваш компилятор должен ее уловить. Здесь нет таблицы поиска и компилятор не встроен.Обратите внимание, что в приведенном выше случае флаг --preserveConstEnums заставит Bread сгенерировать таблицу поиска. Однако его значения по-прежнему будут встроены.
объявить перечисление
Как и в случае с другими вариантами использования
declare
, неdeclare
генерирует код и ожидает, что вы определили фактический код где-то еще. Это не создает таблицу поиска:Wine.Red
излучаетсяWine.Red
в JavaScript, но не будет никакой справочной таблицы Wine для ссылки, поэтому это ошибка, если вы не определили ее где-либо еще.объявить константное перечисление
Это не создает таблицу поиска:
Но он встроен!
Fruit.Apple
выдает 0. Но снова выдаетFruit[0]
ошибку во время выполнения, потому что он не встроен и нет таблицы поиска.Я написал это на этой игровой площадке. Я рекомендую поиграть там, чтобы понять, какой TypeScript какой JavaScript испускает.
источник
Bread[0]
выдает ошибку компилятора: «К члену константного перечисления можно получить доступ только с помощью строкового литерала».