TypeScript学习笔记

枚举

2020-06-29  本文已影响0人  shuaiutopia

反向映射

我们可以通过 Enum[key] 或者 Enum.key 的方式获取到对应的值。typescript 还支持反向映射,即可以通过值来获取键,不过反向映射只支持数字枚举。下面是个例子:

enum Status {
  Success = 200,
  NotFound = 404,
  Error = 500
}

console.log(Status.Success) // 200
console.log(Status[200]) // "Success"
console.log(Status[Status.Success]) // "Success"

枚举成员类型和联合枚举类型

如果一个枚举里所有成员的值都是字面量类型的值,那么这个枚举的每个成员和枚举本身都可以作为类型来使用.

字面量枚举成员是指不带有初始值的常量枚举成员,或者是值被初始化为

注意: 枚举类型与数字类型兼容,并且数字类型与枚举类型兼容。不同枚举类型之间是不兼容的.

// 示例1
enum State {
    on,
    off
}
const s: State = 123 // 数字允许

// 示例2
enum State {
    on = 'abc',
    off = 'def'
}
const s: State = 123 // 数字不允许
const s: State = 'abc' // 字符串不允许
const s: State = State.on // 允许

// 示例3
enum State {
    on = 123,
    off = 'def'
}
const s: State = 123 // 数字允许
const s: State = 111 // 数字允许
const s: State = '123' // 字符串不允许
const s: State = State.on // 允许

枚举成员类型

把符合条件的枚举成员作为类型来使用,例子:

enum ShapeKind {
  Circle,
  Square
}

interface Circle {
  kind: ShapeKind.Circle // 使用 ShapeKind.Circle 作为类型,指定接口须有 kind 字段,且类型为 ShapeKind.Circle
  radius: number
}

interface Square {
  kind: ShapeKind.Square // 同上
  sideLength: number
}

let c: Circle = {
  kind: ShapeKind.Square, // Error! 因为接口 Circle 的 kind 被指定为 ShapeKind.Circle类型,所以这里会报错
  radius: 100
}

联合枚举类型

符合条件的枚举本身可以看作是一个包含所有成员的联合类型,下面例子:

enum E {
    Foo,
    Bar,
}

function f(x: E) { // 此时的 E 相当于 Foo | Bar
    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

// 枚举 Status 里有两个状态
enum Status {
  Off,
  On
}

// 枚举 Animal 里有两个动物
enum Animal {
  Cat,
  Dog
}

// 接口 Light 中定义 status字段,它是 Status 类型,可以是 Off 或者 On 状态
interface Light {
  status: Status
}

let lg1: Light = {
  status: Status.Off // 正确
}

let lg2: Light = {
  status: Animal.Cat // error 不能将类型 Animal.Cat 分配给类型 Status
}

运行时的枚举

枚举是在运行时真正存在的对象,可以把枚举当作对象使用:


enum E {
  A,
  B
}

function func(obj: { A: number }): number {
  return obj.A
}

console.log(func(E)) // 0

代码中,声明了一个函数 func,它的参数是一个对象,且必须包含属性名为 A 的属性,A的值为数值类型。当调用函数 func 时,把枚举 E当作符合条件的实参传入,正确运行。

const 枚举

定义枚举,在经过编译器编译后是一个对象,这个对象我们可以在程序运行时使用。但有时定义枚举可能只是为了让程序可读性更好,而不需要编译后的代码,即不需要编译成对象。typescript 中考虑到这种情况,所以加入了 const enum (完全嵌入的枚举)。typescript官网有一个在线编译器,来看看下面的例子:

// 普通枚举
enum Status{
    Off,
    On
}
// const枚举
const enum Animal{
    Dog,
    Cat
}

const status = Status.On
const animal = Animal.Dog

编译后:

var Status;
(function (Status) {
    Status[Status["Off"] = 0] = "Off";
    Status[Status["On"] = 1] = "On";
})(Status || (Status = {}));
var status = Status.On;
var animal = 0 /* Dog */;

可以看到编译后的代码中并没有像创建 Status 一样创建了 Animal,而是直接把 AnimalDog0 替换到表达式中 Animal.Dog 的位置,这样就节省了生成代码的开销。

特点

const 枚举在编译后因为没有生成相应的对象,因此运行时是不可访问当前枚举对象的,只允许访问枚举成员的值.

// 修饰符 + 关键词 + 枚举名称
const enum NumEnum { a, b, c }

const obj = NumEnum // => Error,运行时不存在
function returnNumber (obj: { a: number }): number {
  return obj.a
}
returnNumber(NumEnum) // => Error,运行时不存在

const A = NumEnum['0'] // => Error,没有生成反向映射,所以不存在该属性
const B = NumEnum['b'] // 正确 => 0/* b */


"compilerOptions": {
  // 保留 const 和 enum 声明该项为 true
  "preserveConstEnums": true
}
// 或者在命令行中添加了 --preserveConstEnums 指令,均会让当前 const 枚举转变为普通枚举
// 即会生成反向映射表;在实际开发中,一般情况你可能需要禁止掉该项,除非想要用于调试;

基于 const 枚举的特点,如果你只是为了生成一组常量并且只需要获取某一个常量, 从性能方面考虑,const 枚举是首选.当然如果你在 tsconfig.json 中指定下面选项:

"compilerOptions": {
  // 保留 const 和 enum 声明该项为 true
  "preserveConstEnums": true
}

或者在命令行中添加了 --preserveConstEnums 指令,均会让当前 const 枚举转变为普通枚举, 即会生成反向映射表. 在实际开发中,一般情况你可能需要禁止掉该项,除非想要用于调试.

上一篇 下一篇

猜你喜欢

热点阅读