热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

TS枚举

枚举使用枚举我们可以定义一些带名字的常量。使用枚举可以清晰地表达意图或创建一组有区别的用例。TypeScript支持数字的和基于字符串的枚举。数字枚举首先我们看看数字枚举,如果你使

枚举

使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript支持数字的和基于字符串的枚举。


数字枚举

首先我们看看数字枚举,如果你使用过其它编程语言应该会很熟悉。

enum Direction {
Up = 1,
Down,
Left,
Right
}

如上,我们定义了一个数字枚举, Up使用初始化为 1。 其余的成员会从 1开始自动增长。 换句话说, Direction.Up的值为 1, Down为 2, Left为 3, Right为 4。

我们还可以完全不使用初始化器:

enum Direction {
Up,
Down,
Left,
Right,
}

现在, Up的值为 0, Down的值为 1等等。 当我们不在乎成员的值的时候,这种自增长的行为是很有用处的,但是要注意每个枚举成员的值都是不同的。

使用枚举很简单:通过枚举的属性来访问枚举成员,和枚举的名字来访问枚举类型:

enum Response {
No = 0,
Yes = 1,
}
function respond(recipient: string, message: Response): void {
// ...
}
respond("Princess Caroline", Response.Yes)

数字枚举可以被混入到 计算过的和常量成员(如下所示)。 简短地说,不带初始化器的枚举或者被放在第一的位置,或者被放在使用了数字常量或其它常量初始化了的枚举后面。 换句话说,下面的情况是不被允许的:

enum E {
A = getSomeValue(),
B, // error! 'A' is not constant-initialized, so 'B' needs an initializer
}

字符串枚举

字符串枚举的概念很简单,但是有细微的 运行时的差别。 在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。

enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}

由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。 换句话说,如果你正在调试并且必须要读一个数字枚举的运行时的值,这个值通常是很难读的 - 它并不能表达有用的信息(尽管 反向映射会有所帮助),字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。


异构枚举(Heterogeneous enums)

从技术的角度来说,枚举可以混合字符串和数字成员,但是似乎你并不会这么做:

enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}

除非你真的想要利用Javascript运行时的行为,否则我们不建议这样做。


计算的和常量成员

每个枚举成员都带有一个值,它可以是 常量或 计算出来的。 当满足如下条件时,枚举成员被当作是常量:

它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值 0:

// E.X is constant:
enum E { X }

它不带有初始化器且它之前的枚举成员是一个 数字常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加1。

// All enum members in 'E1' and 'E2' are constant.
enum E1 { X, Y, Z }
enum E2 {
A = 1, B, C
}

枚举成员使用 常量枚举表达式初始化。 常数枚举表达式是TypeScript表达式的子集,它可以在编译阶段求值。 当一个表达式满足下面条件之一时,它就是一个常量枚举表达式:

一个枚举表达式字面量(主要是字符串字面量或数字字面量)

一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)


带括号的常量枚举表达式

一元运算符 +, -, ~其中之一应用在了常量枚举表达式

常量枚举表达式做为二元运算符 +, -, *, /, %, <<, >>, >>>, &, |, ^的操作对象。 若常数枚举表达式求值后为 NaN或 Infinity,则会在编译阶段报错。

所有其它情况的枚举成员被当作是需要计算得出的值。

enum FileAccess {
// constant members
None,
Read = 1 <<1,
Write = 1 <<2,
ReadWrite = Read | Write,
// computed member
G = "123".length
}

联合枚举与枚举成员的类型

存在一种特殊的非计算的常量枚举成员的子集:字面量枚举成员。 字面量枚举成员是指不带有初始值的常量枚举成员,或者是值被初始化为

任何字符串字面量(例如: "foo", "bar", "baz")

任何数字字面量(例如: 1, 100)

应用了一元 -符号的数字字面量(例如: -1, -100)

当所有枚举成员都拥有字面量枚举值时,它就带有了一种特殊的语义。

首先,枚举成员成为了类型! 例如,我们可以说某些成员 只能是枚举成员的值:

enum ShapeKind {
Circle,
Square,
}
interface Circle {
kind: ShapeKind.Circle;
radius: number;
}
interface Square {
kind: ShapeKind.Square;
sideLength: number;
}
let c: Circle = {
kind: ShapeKind.Square,
// ~~~~~~~~~~~~~~~~ Error!
radius: 100,
}

另一个变化是枚举类型本身变成了每个枚举成员的 联合。 虽然我们还没有讨论[联合类型](./Advanced Types.md#union-types),但你只要知道通过联合枚举,类型系统能够利用这样一个事实,它可以知道枚举里的值的集合。 因此,TypeScript能够捕获在比较值的时候犯的愚蠢的错误。 例如:

enum E {
Foo,
Bar,
}
function f(x: E) {
if (x !== E.Foo || x !== E.Bar) {
// ~~~~~~~~~~~
// Error! Operator '!==' cannot be applied to types 'E.Foo' and 'E.Bar'.
}
}

这个例子里,我们先检查 x是否不是 E.Foo。 如果通过了这个检查,然后 ||会发生短路效果, if语句体里的内容会被执行。 然而,这个检查没有通过,那么 x则 只能为 E.Foo,因此没理由再去检查它是否为 E.Bar。


运行时的枚举

枚举是在运行时真正存在的对象。 例如下面的枚举:

enum E {
X, Y, Z
}
can actually be passed around to functions
function f(obj: { X: number }) {
return obj.X;
}
// Works, since 'E' has a property named 'X' which is a number.
f(E);

反向映射

除了创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了 反向映射,从枚举值到枚举名字。 例如,在下面的例子中:

enum Enum {
A
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"

TypeScript可能会将这段代码编译为下面的Javascript:

var Enum;
(function (Enum) {
Enum[Enum["A"] = 0] = "A";
})(Enum || (Enum = {}));
var a = Enum.A;
var nameOfA = Enum[a]; // "A"

生成的代码中,枚举类型被编译成一个对象,它包含了正向映射( name -> value)和反向映射( value -> name)。 引用枚举成员总会生成为对属性访问并且永远也不会内联代码。

要注意的是 不会为字符串枚举成员生成反向映射。


const枚举

大多数情况下,枚举是十分有效的方案。 然而在某些情况下需求很严格。 为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问,我们可以使用 const枚举。 常量枚举通过在枚举上使用 const修饰符来定义。

const enum Enum {
A = 1,
B = A * 2
}

常量枚举只能使用常量枚举表达式,并且不同于常规的枚举,它们在编译阶段会被删除。 常量枚举成员在使用的地方会被内联进来。 之所以可以这么做是因为,常量枚举不允许包含计算成员。

const enum Directions {
Up,
Down,
Left,
Right
}
let directiOns= [Directions.Up, Directions.Down, Directions.Left, Directions.Right]

生成后的代码为:

var directiOns= [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];

外部枚举

外部枚举用来描述已经存在的枚举类型的形状。

declare enum Enum {
A = 1,
B,
C = 2
}

外部枚举和非外部枚举之间有一个重要的区别,在正常的枚举里,没有初始化方法的成员被当成常数成员。 对于非常数的外部枚举而言,没有初始化方法时被当做需要经过计算的。



推荐阅读
  • STM32代码编写STM32端不需要写关于连接MQTT服务器的代码,连接的工作交给ESP8266来做,STM32只需要通过串口接收和发送数据,间接的与服务器交互。串口三配置串口一已 ... [详细]
  • egg实现登录鉴权(七):权限管理
    权限管理包含三部分:访问页面的权限,操作功能的权限和获取数据权限。页面权限:登录用户所属角色的可访问页面的权限功能权限:登录用户所属角色的可访问页面的操作权限数据权限:登录用户所属 ... [详细]
  • Gradle 是 Android Studio 中默认的构建工具,了解其基本配置对于开发效率的提升至关重要。本文将详细介绍如何在 Gradle 中定义和使用共享变量,以确保项目的一致性和可维护性。 ... [详细]
  • C/C++ 应用程序的安装与卸载解决方案
    本文介绍了如何使用Inno Setup来创建C/C++应用程序的安装程序,包括自动检测并安装所需的运行库,确保应用能够顺利安装和卸载。 ... [详细]
  • 本文详细介绍如何在SSM(Spring + Spring MVC + MyBatis)框架中实现分页功能。包括分页的基本概念、数据准备、前端分页栏的设计与实现、后端分页逻辑的编写以及最终的测试步骤。 ... [详细]
  • 本文详细介绍了如何使用C#实现不同类型的系统服务账户(如Windows服务、计划任务和IIS应用池)的密码重置方法。 ... [详细]
  • 本文详细探讨了Java中HashMap类的hash()方法的工作原理及其重要性,特别是在JDK 7版本中的实现。 ... [详细]
  • 本文详细介绍了在Luat OS中如何实现C与Lua的混合编程,包括在C环境中运行Lua脚本、封装可被Lua调用的C语言库,以及C与Lua之间的数据交互方法。 ... [详细]
  • 如何使用Maven将依赖插件一并打包进JAR文件
    本文详细介绍了在使用Maven构建项目时,如何将所需的依赖插件一同打包进最终的JAR文件中,以避免手动部署依赖库的麻烦。 ... [详细]
  • 题目描述:Balala Power! 时间限制:4000/2000 MS (Java/Other) 内存限制:131072/131072 K (Java/Other)。题目背景及问题描述详见正文。 ... [详细]
  • 本文探讨了Linux环境下线程私有数据(Thread-Specific Data, TSD)的概念及其重要性,介绍了如何通过TSD技术避免多线程间全局变量冲突的问题,并提供了具体的实现方法和示例代码。 ... [详细]
  • 本文探讨了互联网服务提供商(ISP)如何可能篡改或插入用户请求的数据流,并提供了有效的技术手段来防止此类劫持行为,确保网络环境的安全与纯净。 ... [详细]
  • 探讨了一个包含纯虚函数的C++代码片段,分析了其中的语法错误及逻辑问题,并提出了修正方案。 ... [详细]
  • 探讨了在HTML表单中使用元素代替进行表单提交的方法。 ... [详细]
  • 本文探讨了Python类型注解使用率低下的原因,主要归结于历史背景和投资回报率(ROI)的考量。文章不仅分析了类型注解的实际效用,还回顾了Python类型注解的发展历程。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有