Існує чотири різні аспекти для перерахунків у 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 - це один раз, значення якого відомо під час компіляції. Посилання на не обчислених членів завжди накреслені.
Які члени перерахунку обчислюються, а які не обчислюються? По-перше, всі члени constenum є постійними (тобто не обчисленими), як випливає з назви. Для 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. До цієї зміни некомп'ютовані члени неперевірених перерахунків були підкреслені більш агресивно.