Існує чотири різні аспекти для перерахунків у 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 було замінено його числовим еквівалентом:
enum Foo { X = 4 }
var y = Foo.X; // emits "var y = 4";
Специфікація називає цю заміну , я буду називати її вкладною, тому що вона звучить крутіше. Іноді ви не хочете, щоб члени enum були накреслені, наприклад, тому що значення enum може змінитися в майбутній версії API.
Енуми, як вони працюють?
Давайте розбимо це на кожен аспект перерахунку. На жаль, кожен з цих чотирьох розділів збирається посилатися на терміни з усіх інших, тому вам, ймовірно, потрібно буде прочитати всю цю справу не раз.
обчислені проти не обчислені (постійні)
Члени Enum можуть бути обчислені або ні. Специфікація називає незчислених членів постійними , але я буду називати їх невичисленими, щоб уникнути плутанини з const .
Обчислюються член перерахування один, значення якого не відомо під час компіляції. Посилання на обчислені члени, звичайно, не можуть бути накреслені. І навпаки, не обчислений член enum - це один раз, значення якого відомо під час компіляції. Посилання на не обчислених членів завжди накреслені.
Які члени перерахунку обчислюються, а які не обчислюються? По-перше, всі члени const
enum є постійними (тобто не обчисленими), як випливає з назви. Для non-const enum це залежить від того, ви дивитесь на ambient (оголосити) enum або non-ambient enum.
Член declare enum
(наприклад, навколишнє середовище) є постійним, якщо і тільки якщо він має ініціалізатор. В іншому випадку він обчислюється. Зауважте, що в а 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 vs non-const
const
Декларація перерахунку може мати const
модифікатор. Якщо перерахунок є const
, усі посилання на його членів накреслені.
const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always
Перерахунки const не створюють об'єкт пошуку при компілюванні. З цієї причини помилка посилання Foo
на вищезазначений код, за винятком як частини посилання члена. Жоден Foo
об’єкт не буде присутній під час виконання.
неконст
Якщо в декларації перерахунку немає const
модифікатора, посилання на його членів вкладаються лише в тому випадку, якщо член не обчислюється. Неперевірений, недекларований перерахунок створить об'єкт пошуку.
заявляти (оточувати) проти не декларувати
Важлива передмова полягає 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
не відрізняється від а const enum
, за винятком випадків --preserveConstEnums (див. Нижче).
не декларувати
Недекларований перерахунок створює об'єкт пошуку, якщо його немає const
. Вкладиш описано вище.
- прапор conserveConstEnums
Цей прапор має точно один ефект: недекларувати const перерахунки випромінюють об'єкт пошуку. Вкладиш не впливає. Це корисно для налагодження.
Поширені помилки
Найпоширенішою помилкою є використання a, 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/isissue/2183 ), щоб змусити всіх членів незадекларованих перерахунків, які не вважають const, трактуватись як обчислені, навіть якщо вони явно ініціалізовані з літералом. Це, так би мовити, немовля, що робить розкладну поведінку більш передбачуваним і більш чітко відокремлює концепцію const enum
від звичайної enum
. До цієї зміни некомп'ютовані члени неперевірених перерахунків були підкреслені більш агресивно.