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 }
Run Code Online (Sandbox Code Playgroud)
如果我尝试从使用的值Gamma在运行时,我得到一个错误,因为Gamma没有定义,但事实并非案例Delta或Alpha?这里的声明是什么const或declare意味着什么?
还有一个preserveConstEnums编译器标志 - 这与这些标志如何相互作用?
Rya*_*ugh 207
您需要注意TypeScript中的枚举有四个不同的方面.首先,一些定义:
如果你写这个枚举:
enum Foo { X, Y }
Run Code Online (Sandbox Code Playgroud)
TypeScript将发出以下对象:
var Foo;
(function (Foo) {
Foo[Foo["X"] = 0] = "X";
Foo[Foo["Y"] = 1] = "Y";
})(Foo || (Foo = {}));
Run Code Online (Sandbox Code Playgroud)
我将其称为查找对象.它的目的是双重的:作为从映射字符串到数字书写时,如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";
Run Code Online (Sandbox Code Playgroud)
该规范称之为替换,我将其称为内联,因为它听起来更酷.有时您不希望枚举枚举成员,例如因为枚举值可能会在API的未来版本中发生变化.
让我们通过枚举的每个方面来打破这个.不幸的是,这四个部分中的每一部分都将引用所有其他部分的术语,因此您可能需要不止一次地阅读这整个部分.
枚举成员可以是否可以计算.规范将非计算成员调用为常量,但我将其称为非计算,以避免与const混淆.
甲计算枚举成员是一个其值在编译时是未知的.当然,不能内联对计算成员的引用.相反,非计算枚举成员的值在编译时是已知的.始终内联对非计算成员的引用.
哪些枚举成员是计算的,哪些是非计算的?首先,const如名称所示,枚举的所有成员都是常量(即非计算的).对于非常量枚举,它取决于您是在查看环境(声明)枚举还是非环境枚举.
当且仅当它具有初始化器时,declare enum(即环境枚举)的成员是常数.否则,计算它.请注意,在a中,只允许使用数字初始值设定项.例:declare enum
declare enum Foo {
X, // Computed
Y = 2, // Non-computed
Z, // Computed! Not 3! Careful!
Q = 1 + 1 // Error
}
Run Code Online (Sandbox Code Playgroud)
最后,始终认为非声明非const枚举的成员是计算的.但是,如果它们在编译时可计算,则它们的初始化表达式会减少到常量.这意味着非const枚举成员永远不会内联(在TypeScript 1.5中更改了此行为,请参阅底部的"TypeScript中的更改")
枚举声明可以包含const修饰符.如果枚举是const,所有对其成员的引用都是内联的.
const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always
Run Code Online (Sandbox Code Playgroud)
const枚举在编译时不会生成查找对象.因此,Foo除了作为成员引用的一部分之外,在上面的代码中引用是错误的.Foo运行时不会出现任何对象.
如果枚举声明没有const修饰符,则仅当成员未计算时才会内联对其成员的引用.非const,非声明枚举将生成查找对象.
一个重要的前言是,declareTypeScript具有非常特殊的含义:该对象存在于其他地方.它用于描述现有对象.使用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
Run Code Online (Sandbox Code Playgroud)
此错误属于"不要欺骗编译器"的类别.如果您没有Foo在运行时命名的对象,请不要写declare enum Foo!
除了--preserveConstEnums(见下文)之外,A declare const enum与a没有区别const enum.
如果不是,则非声明枚举会生成查找对象const.上面描述了内联.
此标志只有一个效果:非声明const枚举将发出查找对象.内联不受影响.这对调试很有用.
最常见的错误是使用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]);
}
Run Code Online (Sandbox Code Playgroud)
记住黄金法则:绝不declare存在实际存在的东西.使用const enum,如果你总是希望内联,或者enum如果你想查找的对象.
在TypeScript 1.4和1.5之间,行为发生了变化(请参阅https://github.com/Microsoft/TypeScript/issues/2183),以使非声明非const枚举的所有成员都被视为计算,即使它们是用文字明确初始化的.这可以说是"未分割宝宝",使内联行为更加可预测,更清晰地将概念const enum与常规概念分开enum.在此更改之前,非const enums的非计算成员被更加积极地内联.
Kat*_*Kat 20
这里有一些事情发生.让我们逐个进行.
enum Cheese { Brie, Cheddar }
Run Code Online (Sandbox Code Playgroud)
首先,一个普通的老枚举.编译为JavaScript时,这将发出一个查找表.
查找表如下所示:
var Cheese;
(function (Cheese) {
Cheese[Cheese["Brie"] = 0] = "Brie";
Cheese[Cheese["Cheddar"] = 1] = "Cheddar";
})(Cheese || (Cheese = {}));
Run Code Online (Sandbox Code Playgroud)
然后,当你使用Cheese.BrieTypeScript时,它会Cheese.Brie在JavaScript中发出,其值为0. Cheese[0]发出Cheese[0]并实际求值为"Brie".
const enum Bread { Rye, Wheat }
Run Code Online (Sandbox Code Playgroud)
实际上没有为此发出代码!它的值是内联的.以下内容在JavaScript中发出值0:
Bread.Rye
Bread['Rye']
Run Code Online (Sandbox Code Playgroud)
const enum由于性能原因,内联可能很有用.
但那怎么样Bread[0]?这将在运行时出错,您的编译器应该捕获它.没有查找表,编译器不在此处内联.
请注意,在上面的情况中, - prepareConstEnums标志将导致Bread发出查找表.它的价值仍然会被内联.
与其他用途一样declare,不declare发出代码,并希望您在其他地方定义实际代码.这不会发出查找表:
declare enum Wine { Red, Wine }
Run Code Online (Sandbox Code Playgroud)
Wine.RedWine.Red在JavaScript中发出,但是没有任何Wine查找表可供引用,所以除非你在别处定义它,否则它是一个错误.
这不会发出查找表:
declare const enum Fruit { Apple, Pear }
Run Code Online (Sandbox Code Playgroud)
但它确实内联!Fruit.Apple发出0.但是Fruit[0]在运行时再次出错,因为它没有内联,也没有查找表.
我在这个操场上写了这篇文章.我建议在那里玩,以了解哪个TypeScript发出哪个JavaScript.
| 归档时间: |
|
| 查看次数: |
23460 次 |
| 最近记录: |