Вы должны знать четыре различных аспекта перечислений в TypeScript. Для начала несколько определений:
"объект поиска"
Если вы напишете это перечисление:
enum Foo { X, Y }
TypeScript выдаст следующий объект:
var Foo;
(function (Foo) {
Foo[Foo["X"] = 0] = "X";
Foo[Foo["Y"] = 1] = "Y";
})(Foo || (Foo = {}));
Я буду называть его поисковым объектом . Его цель двоякая: служить отображением строк в числа , например, при записи Foo.Xили Foo['X'], и служить отображением чисел в строки . Это обратное сопоставление полезно для целей отладки или ведения журнала - вы часто будете иметь значение 0или 1и хотите получить соответствующую строку "X"или "Y".
"объявить" или " окружающий "
В TypeScript вы можете «объявлять» вещи, о которых должен знать компилятор, но не генерировать код на самом деле. Это полезно, когда у вас есть библиотеки, такие как jQuery, которые определяют некоторый объект (например $), о котором вы хотите ввести информацию, но не нуждаетесь в каком-либо коде, созданном компилятором. Спецификация и другая документация ссылаются на сделанные таким образом объявления как на «окружающий» контекст; Важно отметить, что все объявления в .d.tsфайле являются «внешними» (либо требуют явного declareмодификатора, либо имеют его неявно, в зависимости от типа объявления).
"встраивание"
По соображениям производительности и размера кода часто предпочтительнее иметь ссылку на член перечисления, замененную его числовым эквивалентом при компиляции:
enum Foo { X = 4 }
var y = Foo.X; // emits "var y = 4";
В спецификации эта подстановка называется , я назову ее встраиванием, потому что она звучит круче. Иногда вам не нужно, чтобы члены перечисления были встроены, например, потому что значение перечисления может измениться в будущей версии API.
Enums, как они работают?
Давайте разберем это по каждому аспекту перечисления. К сожалению, каждый из этих четырех разделов будет ссылаться на термины из всех остальных, поэтому вам, вероятно, придется прочитать все это целиком более одного раза.
вычисленное vs невычисленное (константа)
Члены перечисления могут быть вычислены или нет. Спецификация вызовов , не вычисляемые члены постоянной , но я буду называть их не вычисленный , чтобы избежать путаницы с сопзЬ .
Вычисляются член перечисления один, значение которого не известно во время компиляции. Ссылки на вычисляемые члены, конечно, не могут быть встроены. И наоборот, не-вычисленный членом является перечислением раз, значение которого будет известно во время компиляции. Ссылки на невычисляемые элементы всегда встраиваются.
Какие члены перечисления вычисляются, а какие не вычисляются? Во-первых, все члены constперечисления постоянны (т. Е. Не вычисляются), как следует из названия. Для неконстантного перечисления это зависит от того, смотрите ли вы на внешнее (объявленное) перечисление или на внешнее перечисление.
Член declare enum(то есть окружающее перечисление) является константой тогда и только тогда, когда у него есть инициализатор. В противном случае он вычисляется. Обратите внимание, что в a declare enumразрешены только числовые инициализаторы. Пример:
declare enum Foo {
X, // Computed
Y = 2, // Non-computed
Z, // Computed! Not 3! Careful!
Q = 1 + 1 // Error
}
Наконец, элементы перечислений, не объявленных неконстантными, всегда считаются вычисленными. Однако их инициализирующие выражения сокращаются до констант, если они вычислимы во время компиляции. Это означает, что неконстантные члены перечисления никогда не встраиваются (это поведение изменилось в TypeScript 1.5, см. «Изменения в TypeScript» внизу)
константа против неконстантной
Const
Объявление перечисления может иметь constмодификатор. Если перечисление есть const, все ссылки на его члены встроены.
const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always
Перечисления const не создают объект поиска при компиляции. По этой причине ссылка Fooв приведенном выше коде является ошибкой, за исключением ссылки на член. FooВо время выполнения не будет никаких объектов.
неконстантный
Если объявление перечисления не имеет constмодификатора, ссылки на его элементы встроены, только если член не вычисляется. Неконстантное перечисление без объявления создаст объект поиска.
объявить (окружающий) vs не объявить
Важное предисловие состоит в том, что declareв TypeScript есть очень конкретное значение: этот объект существует где-то еще . Это для описания существующих объектов. Использование declareдля определения объектов, которые на самом деле не существуют, может иметь плохие последствия; мы рассмотрим их позже.
объявить
A declare enumне будет генерировать поисковый объект. Ссылки на его элементы встроены, если эти элементы вычисляются (см. Выше о вычисленных и невычисленных).
Важно отметить , что другие формы ссылки на declare enum будут разрешены, например , этот код не ошибка компиляции , но будет не в состоянии во время выполнения:
// Note: Assume no other file has actually created a Foo var at runtime
declare enum Foo { Bar }
var s = 'Bar';
var b = Foo[s]; // Fails
Эта ошибка относится к категории «Не лгите компилятору». Если у вас нет объекта, названного Fooво время выполнения, не пишите declare enum Foo!
A declare const enumне отличается от a const enum, за исключением случая --preserveConstEnums (см. Ниже).
не объявлять
Перечисление без объявления создает объект поиска, если это не так const. Встраивание описано выше.
--preserveConstEnums флаг
Этот флаг имеет только один эффект: перечисления констант без объявления будут генерировать объект поиска. Встраивание не затрагивается. Это полезно для отладки.
Общие ошибки
Самая распространенная ошибка - использовать declare enumвместо обычного enumили const enumболее подходящего. Распространенная форма такова:
module MyModule {
// Claiming this enum exists with 'declare', but it doesn't...
export declare enum Lies {
Foo = 0,
Bar = 1
}
var x = Lies.Foo; // Depend on inlining
}
module SomeOtherCode {
// x ends up as 'undefined' at runtime
import x = MyModule.Lies;
// Try to use lookup object, which ought to exist
// runtime error, canot read property 0 of undefined
console.log(x[x.Foo]);
}
Помните золотое правило: никогда declareто, чего на самом деле не существует . Используйте, const enumесли вы всегда хотите встраивать, или enumесли вам нужен объект поиска.
Изменения в TypeScript
Между TypeScript 1.4 и 1.5 было изменение в поведении (см. Https://github.com/Microsoft/TypeScript/issues/2183 ), чтобы все члены не объявленных неконстантных перечислений считались вычисленными, даже если они явно инициализируются литералом. Это, так сказать, «нерасщепляет ребенка», делая встраиваемое поведение более предсказуемым и более четко отделяя концепцию const enumот обычного enum. До этого изменения невычисляемые члены неконстантных перечислений встраивались более агрессивно.