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)字符串、数字。而且它是类型平安的。