TypeScript 枚举
<p> 我们常常会有这样的场景,比如与后端开发约定订单的状态开始是0,未结账是1,运输中是2,运输完成是3,已收货是4。这样的纯数字会使得代码缺乏可读性。枚举就用于这样的场景。枚举可以让我们定义一些名字有意义的常量。使用枚举可以清晰地表达我们的意图。TypeScript支持基于数字枚举和字符串的枚举。</p><p> </p>
<p>⒈数字枚举</p>
<p> 首先我们举例来看数字枚举</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum OrderStatus{
Start </span>= 1<span style="color: rgba(0, 0, 0, 1)">,
Unpaid,
Shipping,
Shipped,
Complete,
}</span></pre>
</div>
<p> 就像上面这样,我们通过数字来表达了订单状态。在实际的代码编写时,我们就直接使用OrderStatus.Start来代替原本的数字1,这就使得代码具备了相当的可读性,甚至可以免去冗余的注释。</p>
<p> 在上面的代码中还使用了一个小技巧,当只写Start = 1时,后面的枚举变量就是递增的,当我们不在乎成员的值的时候,这种自增长的行为是很有用处的,但是要注意每个枚举成员的值都是不同的,但你也可以明确地写出每个枚举变量的数字,这取决于具体的业务场景。</p>
<p> 当我们连第一个枚举变量的值都不写的时候,初始值将会为0,后面的枚举类型再依次递增。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum OrderStatus{
Start,
Unpaid,
Shipping,
Shipped,
Complete,
}</span></pre>
</div>
<p> 现在OrderStatus.Start的值为0,通常情况下,在使用这样的写法时,我们其实是不在乎成员变量的具体值的,我们只知道这些值是不同的。</p>
<p> 枚举类型中的值必须是确定的,比如,像下面这样的写法是不被允许的。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum Example{
A </span>=<span style="color: rgba(0, 0, 0, 1)"> hello(),
b,
}</span></pre>
</div>
<p> 类似这样没有给出确定值的写法在TypeScript中都是不被允许的。</p>
<p> </p>
<p>⒉字符串枚举</p>
<p> 字符串枚举的概念与数字枚举有一些细微的差别</p>
<p> 在一个字符串枚举中,所有成员必须都是字符串字面量,且字符串枚举没有递增的含义,字符串枚举的成员都必须手动初始化。 </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum OrderStatus{
Start </span>= 'Start'<span style="color: rgba(0, 0, 0, 1)">,
Unpaid </span>= 'Unpaid'<span style="color: rgba(0, 0, 0, 1)">,
Shipping </span>= 'Shipping'<span style="color: rgba(0, 0, 0, 1)">,
Shipped </span>= 'Shipped'<span style="color: rgba(0, 0, 0, 1)">,
Complete </span>= 'Complete'<span style="color: rgba(0, 0, 0, 1)">,
}</span></pre>
</div>
<p> 虽然字符串枚举显得繁琐,但相比数字枚举,由于字符串枚举没有自增长的行为,因此字符串枚举能进一步提升可读性,如果你正在调试并且必须要读一个数字枚举的运行时的值,这个值通常是很难读的,即使读取出来它也并不能表达有用的信息,而字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。这使得调试变得更容易。所以在实际的开发中,大家更喜欢使用字符串枚举。</p>
<p> </p>
<p>⒊异构枚举</p>
<p> 从技术的角度来说,枚举可以混合字符串和数字成员,但是似乎你并不会这么做,除非你真的想要利用JavaScript运行时的行为,否则我们不建议这样做。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum OrderStatus{
Start </span>= 'Start'<span style="color: rgba(0, 0, 0, 1)">,
Unpaid </span>= 1<span style="color: rgba(0, 0, 0, 1)">,
Shipping,
Shipped,
Complete,
}</span></pre>
</div>
<p>⒋枚举成员的值</p>
<p> 每个枚举成员都带有一个值,它可以是<strong> 常量</strong>或 经过<strong>计算出来的</strong>。</p>
<p> 当满足如下条件时,枚举成员被当作是<strong>常量</strong>:</p>
<p> 1.当前枚举成员是枚举的第一个成员且没有初始化器,这种情况下它将被赋予常量值 <code>0</code>:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum OrderStatus{
Start,</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">OrderStatus.Start is 0</span>
<span style="color: rgba(0, 0, 0, 1)"> Unpaid,
Shipping,
Shipped,
Complete,
}</span></pre>
</div>
<p> 2.当前枚举成员不带有初始化器且它之前的枚举成员的值是一个<strong> <em>数字</em></strong>常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加1。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum OrderStatus{
Start </span>= 0<span style="color: rgba(0, 0, 0, 1)">,
Unpaid, </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">OrderStatus.Unpaid is 1</span>
<span style="color: rgba(0, 0, 0, 1)"> Shipping,
Shipped,
Complete,
}</span></pre>
</div>
<p> 3.枚举成员使用 <em>常量枚举表达式</em>初始化。 常数枚举表达式是TypeScript表达式的子集,它可以在编译阶段求值。 当一个表达式满足下面条件之一时,它就是一个常量枚举表达式:</p>
<ul>
<li style="list-style-type: none">
<ul>
<li>一个枚举表达式字面量(主要是字符串字面量或数字字面量)</li>
<li>一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)</li>
<li>带括号的常量枚举表达式</li>
<li>一元运算符 <code>+</code>, <code>-</code>, <code>~</code>其中之一应用在了常量枚举表达式</li>
<li>常量枚举表达式做为二元运算符 <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>%</code>, <code><<</code>, <code>>></code>, <code>>>></code>, <code>&</code>, <code>|</code>, <code>^</code>的操作对象。 若常数枚举表达式求值后为 <code>NaN</code>或 <code>Infinity</code>,则会在编译阶段报错。</li>
</ul>
</li>
</ul>
<p> 当以上的条件均不满足的情况下,枚举成员被当作是需要计算得出的值:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum OrderStatus{
None,
Read </span>= 1 << 1<span style="color: rgba(0, 0, 0, 1)">,
Write </span>= 1 << 2<span style="color: rgba(0, 0, 0, 1)">,
ReadWrite</span>= Read |<span style="color: rgba(0, 0, 0, 1)"> Write,
G </span>= "123"<span style="color: rgba(0, 0, 0, 1)">.length
}</span></pre>
</div>
<p>⒌枚举成员类型和联合枚举类型</p>
<p> 如果一个枚举里所有成员的值都是字面量类型的值,那么这个枚举的每个成员和枚举本身都可以作为类型来使用。字面量枚举成员需满足以下条件:</p>
<ul>
<li style="list-style-type: none">
<ul>
<li>不带初始值的枚举成员,例如 enum E { A }</li>
<li>值为字符串字面量,例如 enum E { A = 'hello' }</li>
<li>值为数字字面量,或者带有一元 <code>- </code>符号的数字字面量,例如 enum E { A = 1 },enum E { A = -1 }</li>
</ul>
</li>
</ul>
<p> 1.枚举成员类型</p>
<p> 把符合条件的枚举成员作为类型来使用,例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum ShapeKind {
Circle,
Square
}
interface Circle {
kind: ShapeKind.Circle </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用 ShapeKind.Circle 作为类型,指定接口须有 kind 字段,且类型为 ShapeKind.Circle</span>
<span style="color: rgba(0, 0, 0, 1)">radius: number
}
interface Square {
kind: ShapeKind.Square </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 同上</span>
<span style="color: rgba(0, 0, 0, 1)">sideLength: number
}
let c: Circle </span>=<span style="color: rgba(0, 0, 0, 1)"> {
kind: ShapeKind.Square, </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Error! 因为接口 Circle 的 kind 被指定为 ShapeKind.Circle类型,所以这里会报错</span>
radius: 100<span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p> interface是定义接口,现在简单了解即可。</p>
<p> 2.联合枚举类型</p>
<p> 符合条件的枚举本身可以看作是一个包含所有成员的联合类型,下面例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 枚举 Status 里有两个状态</span>
<span style="color: rgba(0, 0, 0, 1)">enum Status {
Off,
On
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 枚举 Animal 里有两个动物</span>
<span style="color: rgba(0, 0, 0, 1)">enum Animal {
Cat,
Dog
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 接口 Light 中定义 status字段,它是 Status 类型,可以是 Off 或者 On 状态</span>
<span style="color: rgba(0, 0, 0, 1)">interface Light {
status: Status
}
let lg1: Light </span>=<span style="color: rgba(0, 0, 0, 1)"> {
status: Status.Off </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 正确</span>
<span style="color: rgba(0, 0, 0, 1)">}
let lg2: Light </span>=<span style="color: rgba(0, 0, 0, 1)"> {
status: Animal.Cat </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> error 不能将类型 Animal.Cat 分配给类型 Status</span>
}</pre>
</div>
<p>⒍运行时的枚举</p>
<p> 枚举是在运行时真正存在的对象,可以把枚举当作对象使用:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum E {
A,
B
}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> func(obj: { A: number }): number {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> obj.A
}
console.log(func(E)) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 0</span></pre>
</div>
<p> 代码中,声明了一个函数 func,它的参数是一个对象,且必须包含属性名为 A 的属性,A 的值为数值类型。当调用函数 func 时,把枚举 E 当作符合条件的实参传入,正确运行。</p>
<p>⒎反向映射</p>
<p> 反向映射是<strong>数字枚举</strong>的一个技巧,这得益于TypeScript在实现数字枚举时的代码编译。</p>
<p> 例如当我们设定了如下一个枚举时:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum OrderStatus{
A
}</span></pre>
</div>
<p> TypeScript编译器将这段代码编译到JavaScript时是这样的:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> Enum;
(</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (Enum) {
Enum = 0] = "A"<span style="color: rgba(0, 0, 0, 1)">;
})(Enum </span>|| (Enum = {}));</pre>
</div>
<p> 生成的代码中,枚举类型被编译成一个对象,它包含了正向映射( <code>key</code> -> <code>value</code>)和反向映射( <code>value</code> -> <code>key</code>)。所以我们既可以通过属性名获取属性值,也可以通过属性值获取属性名。</p>
<div class="cnblogs_code">
<pre>let a =<span style="color: rgba(0, 0, 0, 1)"> OrderStatus.A;
let b </span>= OrderStatus;
console.log(a); //0
console.log(b); //A</span></pre>
</div>
<p> 注意:字符串编译后并没有这样的特性,因此,在字符串枚举中没有反向映射。</p>
<p>⒏const 枚举</p>
<p> 定义的枚举,在经过编译器编译后是一个对象,这个对象我们可以在程序运行时使用,前面有说到。但有时定义枚举可能只是为了让程序可读性更好,而不需要编译后的代码,即不需要编译成对象。typescript中考虑到这种情况,所以加入了 const enum (完全嵌入的枚举)。typescript官网有一个在线编译器,来看看下面的例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">enum Status{
Off,
On
}
const enum Animal{
Dog,
Cat
}
const status </span>=<span style="color: rgba(0, 0, 0, 1)"> Status.On
const animal </span>= Animal.Dog</pre>
</div>
<p> 这段代码编译成JavaScript后是这样的:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> Status;
(</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (Status) {
Status = 0] = "Off"<span style="color: rgba(0, 0, 0, 1)">;
Status = 1] = "On"<span style="color: rgba(0, 0, 0, 1)">;
})(Status </span>|| (Status =<span style="color: rgba(0, 0, 0, 1)"> {}));
</span><span style="color: rgba(0, 0, 255, 1)">var</span> status =<span style="color: rgba(0, 0, 0, 1)"> Status.On;
</span><span style="color: rgba(0, 0, 255, 1)">var</span> animal = 0 <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Dog </span><span style="color: rgba(0, 128, 0, 1)">*/</span>;</pre>
</div>
<p> 可以看到编译后的代码中并没有像创建Status一样创建了Animal,而是直接把 Animal 中 Dog 值 0 替换到表达式中 Animal.Dog 的位置,这样就节省了生成代码的开销。</p>
<p>⒐外部枚举</p>
<p> 外部枚举用来描述已经存在的枚举类型的形状。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">declare enum Enum {
A </span>= 1<span style="color: rgba(0, 0, 0, 1)">,
B,
C </span>= 2<span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p> 外部枚举和非外部枚举之间有一个重要的区别,在正常的枚举里,没有初始化方法的成员被当成常数成员。 对于非常数的外部枚举而言,没有初始化方法时被当做需要经过计算的。</p>
<p> </p>
<p> 借鉴文章:https://www.cnblogs.com/wjaaron/p/11672764.html</p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p><br><br>
来源:https://www.cnblogs.com/fanqisoft/p/11933327.html
頁:
[1]