TypeScript引入了很多静态编译语言的特性,比如class(现在是JavaScript的一部分了),interface, generics和union types等。

但是今天有一个类型需要着重讨论下,这就是enum。

对于很多的静态语言来说,枚举是一个很非常常见的语言特性。比如,c,c#,java和swift。枚举就是你在代码里可以用的一组常量。

我们用TypeScript来新建一个enum来代表一周的几天:

enum DayOfWeek {
  Sunday,
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday
};

这个枚举使用enum关键字声明,后面跟着DayOfWeek名称。然后我们定义枚举里可以使用的常量。

现在我们定义一个方法,接受这个枚举类型的参数,来判断传入的参数是不是周末。

function isItTheWeekend(day: DayOfWeek) {
  switch (day) {
    case DayOfWeek.Sunday:
    case DayOfWeek.Saturday:
      return true;
 
    default:
      return false;
  }
}

最后,我们可以这要用:

console.log(isItTheWeekend(DayOfWeek.Monday)); // log: false

对于消除程序里的魔法字符串来说,这是一个非常有用的方法。

但是,事情远不是我们想的这么简单。下面的代码调用会在TypeScript编译之后得到什么呢?

console.log(isItTheWeekend(2)); // is this valid?

知道结果你会吓一跳。这样的调用是符合TypeScript规则的,编译器也会顺利编译。

发生了什么呢?

上面的情况可能会让你认为你发现了一个TypeScript的bug。其实TypeScript就是这么设计的。

我们这里新建了一个数字枚举,而且我们可以在TypeScript Playground里看看编译出来的结果是什么:

var DayOfWeek;
(function (DayOfWeek) {
    DayOfWeek[DayOfWeek["Sunday"] = 0] = "Sunday";
    DayOfWeek[DayOfWeek["Monday"] = 1] = "Monday";
    DayOfWeek[DayOfWeek["Tuesday"] = 2] = "Tuesday";
    DayOfWeek[DayOfWeek["Wednesday"] = 3] = "Wednesday";
    DayOfWeek[DayOfWeek["Thursday"] = 4] = "Thursday";
    DayOfWeek[DayOfWeek["Friday"] = 5] = "Friday";
    DayOfWeek[DayOfWeek["Saturday"] = 6] = "Saturday";
})(DayOfWeek || (DayOfWeek = {}));

运行结果是:

事实上枚举就是一个JavaScript对象。

这个对象的属性就是根据我进定义的枚举常量生成,还根据定义的顺序生成了对应的数字(顺序,Sunday是0,Saturday是6)。这个对象也有数字作为key,对应的常量字符串作为值的属性。

因此,我们可以给上面的方法传入数字,数字映射到对应的枚举值。枚举既是一个数字常量也是一个字符串常量。

什么时候用

如果一个方法接收一个枚举类型参数,但是一个任意的数字就可以通过编译的话。这样的结果显然破坏了TypeScript构建的类型安全体系。这什么时候可以用呢?

假设你有一个服务返回一个JSON串,你想把这个串建模,对应的某个属性是一个枚举。

在你的数据库里存的是数字。定义一个TypeScript枚举可以很容易解决这个问题:

const day: DayOfWeek = 3;

这个在赋值时执行的显示的类型转换会把数字转换成枚举的对应常量。也就是说我们在代码里使用这个枚举会让代码更容易读懂。

控制枚举的数字

枚举的成员对应的数字是根据枚举常量定义的顺序生成的。那我们是否可以控制这个数字的值呢?是可以的。

enum FileState {
  Read = 1,
  Write = 2
}

只是描述一个文件可能的状态的枚举。

它可能是读也可能是写状态,我们显示的定义了枚举值。现在就很明确什么样的值是合理的,因为显示定义了。

Bit值

但是还有另一个情况很有用,位值(Bit)。

我们再来看一下这个FileState枚举,给它添加一个新的枚举值ReadWrite:

enum FileState {
  Read = 1,
  Write = 2,
  ReadWrite = 3
}

之后假设有一个方法接受这个类型的参数:

const file = await getFile("/path/to/file", FileState.Read | FileState.Write);

我们在FileState上使用了|操作符。这样我们可以使用位运算来获得一个新的枚举值。在这个例子里面就是3,ReadWrite的值。

事实上,我们可以写的更清楚一些:

enum FileState {
  Read = 1,
  Write = 2,
  ReadWrite = Read | Write
}

这个ReadWrite的值不是写死的,而是位运算得到的。

但是再这样使用枚举的时候要多加小心。

如下的枚举:

enum Foo {
  A = 1,
  B = 2,
  C = 3,
  D = 4,
  E = 5
}

如果要得到E(或者5),可以位运算得到么:Foo.A | Foo.D or Foo.B | Foo.C?

所以如果要用枚举值做位运算,那么明确如何得到这个值。

控制索引

一般情况下,每个枚举值都会有一个默认的数字值。如果需要也可以明确的给这些枚举值赋值。另外,还可以给某部分枚举赋值:

enum DayOfWeek {
  Sunday,
  Monday,
  Tuesday,
  Wednesday = 10,
  Thursday,
  Friday,
  Saturday
}

前几个值是按照位置赋值,Sunday到TuesDay是0到2.之后在Wednesday给了一个新值,从这开始每个值都递增1. 这就可能会出现问题了:

enum DayOfWeek {
  Sunday,
  Monday,
  Tuesday,
  Wednesday = 10,
  Thursday = 2,
  Friday,
  Saturday
}

Tuesday赋值为2,生成的JavaScript是什么样子呢:

var DayOfWeek;
(function (DayOfWeek) {
    DayOfWeek[DayOfWeek["Sunday"] = 0] = "Sunday";
    DayOfWeek[DayOfWeek["Monday"] = 1] = "Monday";
    DayOfWeek[DayOfWeek["Tuesday"] = 2] = "Tuesday";
    DayOfWeek[DayOfWeek["Wednesday"] = 10] = "Wednesday";
    DayOfWeek[DayOfWeek["Thursday"] = 2] = "Thursday";
    DayOfWeek[DayOfWeek["Friday"] = 3] = "Friday";
    DayOfWeek[DayOfWeek["Saturday"] = 4] = "Saturday";
})(DayOfWeek || (DayOfWeek = {}));

看起来Tuesday和Thursday的数值都是2。

所以,需要显示的设定数值。

非数字枚举

目前为止,我们只讨论了数值枚举,但是枚举的值不一定非的是数字。它也可以是任何常量或者计算值:

enum DayOfWeek {
  Sunday = "Sun",
  Monday = "Mon",
  Tuesday = "Tues",
  Wednesday = "Wed",
  Thursday = "Thurs",
  Friday = "Fri",
  Saturday = "Sat"
}

现在就不能给isItTheWeekend方法穿数字参数了。这个枚举已经不再是数字枚举。然而,我们也不能传任意字符串进去,因为枚举知道什么样的值才是合理的。

这样也带来另外一个问题:

const day: DayOfWeek = "Mon";

这样是行不通的。

字符串并不能直接给枚举赋值,而是需要一个显示的类型转换:

const day = "Mon" as DayOfWeek;
能不能给它赋其他值呢?事实上枚举可以有很多类型的值:

enum Confusing {
  A,
  B = 1,
  C = 1 << 8,
  D = 1   2,
  E = "Hello World".length
}

这个例子的枚举值都是数字。但是这些数字值可以直接赋值,也可以是计算值,或者是字符串的length属性。如果都是常量的话,那么就可以是多种类型的值:

enum MoreConfusion {
  A,
  B = 2,
  C = "C"
}

这种情况就很难让人理解枚举后面的数据是怎么工作的。所以,最好不要用这样的枚举。

结论

TypeScript的枚举是对JavaScript的一个很好地补充,使用得当将非常有用。它将有助于清理代码中存在的魔术值(magic values)字符串、数字。而且它是类型安全的。

到此这篇关于为什么TypeScript的Enum会出现问题的文章就介绍到这了,更多相关TypeScript Enum内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

为什么TypeScript的Enum会出现问题的更多相关文章

  1. ios – 从枚举类型’enum UIDeviceOrientation’到不同的枚举类型’UIInterfaceOrientation”enum UIInterfaceOrientation的隐式转换

    我正在使用Xcode5.0.2并在尝试编译我的目标C代码时收到以下警告:Implicitconversionfromenumerationtype‘enumUIDeviceOrientation’todifferentenumerationtype‘UIInterfaceOrientation’(aka‘enumUIInterfaceOrientation’)警告在viewController.m

  2. ios – 如何在迅捷中比较Enum?

    在Objective-C中这很好用无法在Swift中编译它要么IOSSDK中的ALAuthorizationStatus定义解决方法比较运算符==返回Bool,而不是布尔值.以下编译:(就个人而言,我发现Swift编译器的错误消息有时令人困惑.在这种情况下,问题不是==的参数,而是错误的返回类型.)实际上,由于自动类型推断,还应编译以下内容:但它失败了编译器错误“无法找到成员’授权’”,除非你显式

  3. Swift实现菜单的多选

    Swift中的option类型Swift的enum不支持多选,所以能够多选的option类型应该用struct:RawOptionSet来实现。官方的SDK正是采取这种方法移植的。正确的写法以上例子中的代码,用swift正确的写法如下:letoptions:NsstringDrawingOptions=.UsesLineFragmentOrigin|.UsesFontLeadingletboundingRect=string.bridgetoObjectiveC().boundingRectWithSiz

  4. Swift的enum枚举类型介绍

    声明枚举类型的几种方法和使用:一:二:

  5. swift enum 和OC兼容

    虽然说swift是基于OC的,但是swift和OC还是有很多去别的,今天我们来说一下枚举变量下面是某个免费短信验证码的验证方法的返回状态这是一个正常的OC枚举写法,但是如果你在swift里面调用这个写在OC里面的枚举想进行操作的话,那么对不起,你会收到各种错误提示比如你不能这样会报下面的错误(这个绝不是.Success调用错的愿意)你也不能这样会报下面的错误但是下面的方法可以进行调用,但是我没有找

  6. Swift语言中class、struct、enum的联系与区别

    相同点三者都可以拥有属性和方法都可以拥有函数class和struct可以拥有自己的构造器不同点类可以继承类可以内省和转型struct、enum为值类型,class为引用类型类方法用class关键词声明,enum、struct用static关键词用mutaing关键词声明要修改struct、enum内容的方法结构体适合基本数据类型

  7. Swift中一个类中的枚举enum类型的数据该如何实现序列化NSCoder

    简述昨天在开发中遇到了这样一个问题,需要用NSUserDefaults持久化一些数据,其中需要保存一个自己定义的类对象。结束其实枚举本来就是一个Int,因此我们将其声明为Int型就可以根据Int值初始化了,以此实现序列化和反序列化。

  8. Swift 中枚举高级用法及实践

    和switch语句类似,Swift中的枚举乍看之下更像是C语言中枚举的进阶版本,即允许你定义一种类型,用于表示普通事情中某种用例。不过深入挖掘之后,凭借Swift背后特别的设计理念,相比较C语言枚举来说其在实际场景中的应用更为广泛。特别是作为强大的工具,Swift中的枚举能够清晰表达代码的意图。最后我们还会大致了解下Swift标准库中枚举是如何被使用的。小结至此,我们已经大致了解了Swift中枚举语法的基本用例。

  9. Swift中enum自定义类型的实现

    不过Swift给我对enum类型自定义扩展的一种方法,就是实现RawRepresentable协议!下面我们再假想一个场景:一个记录中包含一个type,用来说明记录类型,与上面类似,每一个类型都有name和logo两部分.我们可以选择自定义一个结构来搞定:不过在这里,使用元组更为简单,以下是完整的实现:于是乎如果我们想要用type来初始化UI,我们可以这么写:效果如下:

  10. [Swift] Enum 好用, Enum 可以更易用

    只需要写少量的几行声明:如下.选中,右键,选择"Services|generate_enum"然后如下代码就自动生成了:怎么样?当然还有其他选项可以使用.如果你有其他需要麻烦告诉我.当然有PR最好了.最后来张动图感受一下:代码在:OSCGitGithub

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部