Enums 不同的枚举变量如何在TypeScript中工作?
TypeScript有许多不同的方法来定义枚举:Enums 不同的枚举变量如何在TypeScript中工作?,enums,typescript,Enums,Typescript,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是什么意思 还有一个preserveConstE
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
编译器标志——它是如何与它们交互的?在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的未来版本中更改
枚举,它们是如何工作的? 让我们按枚举的每个方面来分解它。不幸的是,这四个部分中的每一部分都会引用所有其他部分的术语,因此您可能需要不止一次地阅读整个内容 计算与非计算(常数) 枚举成员可以计算也可以不计算。规范将非计算成员称为常量,但我将它们称为非计算成员,以避免与常量混淆 计算枚举成员是编译时其值未知的成员。当然,对计算成员的引用不能内联。相反,非计算枚举成员在编译时的值已知。对非计算成员的引用始终是内联的
const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always
哪些枚举成员是计算的,哪些是非计算的?首先,顾名思义,const
enum的所有成员都是常量(即非计算)。对于非常量枚举,这取决于您查看的是环境(声明)枚举还是非环境枚举
声明枚举的成员(即环境枚举)是常量,当且仅当其具有初始值设定项时。否则,将对其进行计算。请注意,在声明枚举
中,只允许使用数字初始值设定项。例如:
declare enum Foo {
X, // Computed
Y = 2, // Non-computed
Z, // Computed! Not 3! Careful!
Q = 1 + 1 // Error
}
最后,非declare非const枚举的成员总是被认为是计算的。但是,如果它们在编译时是可计算的,则它们的初始化表达式将缩减为常量。这意味着从不内联非常量枚举成员(此行为在TypeScript 1.5中已更改,请参阅底部的“TypeScript中的更改”)
常量与非常量
常数
枚举声明可以有const
修饰符。如果枚举是常量
,则对其成员的所有引用都将内联
const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always
常量枚举在编译时不生成查找对象。因此,在上述代码中引用Foo
是错误的,但作为成员引用的一部分除外。运行时不会出现Foo
对象
非常量
如果枚举声明没有const
修饰符,则仅当成员为非计算成员时,才会内联对其成员的引用。非常量、非声明枚举将生成查找对象
声明(环境)与非声明
一个重要的前言是,TypeScript中的declare
有一个非常具体的含义:这个对象存在于其他地方。它用于描述现有对象。使用declare
定义实际上不存在的对象可能会产生不良后果;我们稍后将探讨这些问题
声明
声明枚举
不会发出查找对象。如果这些成员是计算的,则对其成员的引用是内联的(请参见上文关于计算的与非计算的)
需要注意的是,允许以其他形式引用声明枚举
,例如,此代码不是编译错误,但在运行时会失败:
// 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
declare const-enum
与const-enum
没有区别,除了--preserveConstenum(见下文)的情况
不申报
如果非声明枚举不是const
,它将生成查找对象。内联如上所述
--保留常量枚举标志
此标志只有一个效果:非declare 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]);
}
记住黄金法则:永远不要声明实际上不存在的东西。使用co
var Cheese;
(function (Cheese) {
Cheese[Cheese["Brie"] = 0] = "Brie";
Cheese[Cheese["Cheddar"] = 1] = "Cheddar";
})(Cheese || (Cheese = {}));
const enum Bread { Rye, Wheat }
Bread.Rye
Bread['Rye']
declare enum Wine { Red, Wine }
declare const enum Fruit { Apple, Pear }