开发过程中经常用某几个数值用来代表某些状态。
    比如我们写一个对象表示文章对象:

    1. const postArticle = {
    2. title: 'hello ts',
    3. content: 'ts is a typed syperset of javascript',
    4. status: 2 //2 代表已发布 //1已修改 //0 未发布
    5. }

    用数值代表某种状态有什么问题?

    • 不容易记忆并且容易乱。
    • 假设数值很多,开发时没有写注释的话,就显得不易维护了。

    那么应该如何改进了,这时候就可以用到枚举了。
    在JS中,一般我们用的时候定义一个对象专门存放枚举类型 比如:

    1. const PostStutus = {
    2. Draft : 0,
    3. Unpublished : 1,
    4. Published: 2
    5. }
    6. //用的时候
    7. const postArticle = {
    8. title: 'hello ts',
    9. content: 'ts is a typed syperset of javascript',
    10. status:PostStutus.Published //2 //2 代表已发布 //1已修改 //0 未发布 //3
    11. }

    使用枚举的优点是:

    • 给一组数值 起上一个可以理解的名字 便于使用并且代码得可读性。
    • 一个枚举中只会出现特定的值 不会出现超出值的可能性。

    而在ts中,又是如何定义枚举的呢?

    • 关键词:enum
    • 冒号改为= ``` //样式一 enum postStatus { Draft = 1, Unpublished = 2, Published = 3 }

    // 不写值 就会从0开始累加 enum postStatus2 { Draft, Unpublished, Published }

    // 指定一个值 后面从这个值开始累加 enum postStatus3 { Draft = 3, Unpublished, Published }

    // 字符串枚举 enum postStatus4 { Draft = ‘aaaa’, Unpublished = ‘bbbb’, Published = ‘cccc’ } // 枚举类型会入侵我们的代码 就是编译后 本身类型校验都会被去掉,但是枚举会留在里面 // 枚举类型会编译成一个双向的键值对 对象

    // 推荐使用常量枚举 const enum postStatus5 { Draft = 3, Unpublished, Published } // 而常量枚举枚举被移除掉,使用枚举值的地方被替换成值 status: 3 / Draft / //2//1//0 const post = { title: ‘Hello TypeScript’, status: postStatus4.Draft //2//1//0 }

    1. <a name="SaoWk"></a>
    2. ## 数字枚举
    3. 当我们声明一个枚举类型是,虽然没有给它们赋值,但是它们的值其实是默认的数字类型,而且默认从0开始依次累加:

    enum Direction { Up, Down, Left, Right } console.log(Direction.Up === 0); // true console.log(Direction.Down === 1); // true console.log(Direction.Left === 2); // true console.log(Direction.Right === 3); // true

    1. 因此当我们把第一个值赋值后,后面也会根据第一个值进行累加:

    enum Direction { Up = 10, Down, Left, Right } console.log(Direction.Up, Direction.Down, Direction.Left, Direction.Right); // 10 11 12 13

    1. <a name="OoZqz"></a>
    2. ## 字符串枚举
    3. 枚举类型的值其实也可以是字符串类型:

    enum Direction { Up = ‘Up’, Down = ‘Down’, Left = ‘Left’, Right = ‘Right’ } console.log(Direction[‘Right’], Direction.Up); // Right Up

    1. <a name="W89gu"></a>
    2. ## 异构枚举
    3. 既然我们已经有了字符串枚举和数字枚举,那么这两个枚举是不是可以混合使用呢?

    enum BooleanLikeHeterogeneousEnum { No = 0, Yes = “YES”, }

    1. 是的,这样也是没问题的,通常情况下我们很少会这样使用枚举,但是从技术的角度来说,它是可行的。
    2. <a name="0fzdv"></a>
    3. ## 反向映射
    4. 我们看一个例子:

    enum Direction { Up, Down, Left, Right } console.log(Direction.Up === 0); // true console.log(Direction.Down === 1); // true console.log(Direction.Left === 2); // true console.log(Direction.Right === 3); // true

    1. 这就是我们数字枚举那一部分的例子,我们可以通过枚举名字获取枚举值,这当然看起来没问题,那么能不能通过枚举值获取枚举名字呢?<br />是可以的:

    enum Direction { Up, Down, Left, Right } console.log(Direction[‘Up’]); // 0

    1. 这就很奇怪了,我们印象中一个 JavaScript 对象一般都是正向映射的,即 `name => value`,为什么在枚举中是可以正反向同时映射的?即 `name <=> value`。<br />我们往下看,通过了解枚举的本质,我们就可以理解这种正反向同时映射的特性了。
    2. <a name="oaBjL"></a>
    3. ## 枚举的本质
    4. 以上面的 `Direction` 枚举类型为例,我们不妨看一下枚举类型被编译为 JavaScript 后是什么样子:

    var Direction; (function (Direction) { Direction[Direction[“Up”] = 10] = “Up”; Direction[Direction[“Down”] = 11] = “Down”; Direction[Direction[“Left”] = 12] = “Left”; Direction[Direction[“Right”] = 13] = “Right”; })(Direction || (Direction = {}));

    1. 这个编译后的代码可能看起来比较复杂,不过我们可以把`Direction`看成一个对象,比如我们在 TypeScript 中做几个小实验:

    enum Direction { Up = 10, Down, Left, Right } console.log(Direction[10], Direction[‘Right’]); // Up 13

    1. 原因就在编译后的 JavaScript 中体现出来了,因为 `Direction[Direction["Up"] = 10] = "Up"` 也就是 `Direction[10] = "Up"` ,所以我们可以把枚举类型看成一个JavaScript对象,而由于其特殊的构造,导致其拥有正反向同时映射的特性。
    2. <a name="jKxOh"></a>
    3. ## 常量枚举
    4. 枚举其实可以被 `const` 声明为常量的,这样有什么好处?我们看以下例子:

    const enum Direction { Up = ‘Up’, Down = ‘Down’, Left = ‘Left’, Right = ‘Right’ } const a = Direction.Up;

    1. 大家猜一下它被编译为 JavaScript 后是怎样的?

    var a = “Up”;

    1. 我们在上面看到枚举类型会被编译为 JavaScript 对象,怎么这里没有了?<br />这就是常量枚举的作用,因为下面的变量 `a` 已经使用过了枚举类型,之后就没有用了,也没有必要存在与 JavaScript 中了, TypeScript 在这一步就把 `Direction` 去掉了,我们直接使用 `Direction` 的值即可,这是性能提升的一个方案。
    2. > 如果你非要 TypeScript 保留对象 Direction ,那么可以添加编译选项 `--preserveConstEnums`
    3. <a name="renGW"></a>
    4. ## 联合枚举与枚举成员的类型
    5. 我们假设枚举的所有成员都是字面量类型的值,那么枚举的每个成员和枚举值本身都可以作为类型来使用,
    6. - 任何字符串字面量,如:

    const enum Direction { Up = ‘Up’, Down = ‘Down’, Left = ‘Left’, Right = ‘Right’ }

    1. - 任何数字字面量,如:

    enum Direction { Up, Down, Left, Right }

    1. - 应用了一元`-`符号的数字字面量,如:

    enum Direction { Up = -1, Down = -2, Left = -3, Right = -4, }

    1. <a name="iOA6z"></a>
    2. ### 枚举成员类型
    3. 当所有枚举成员都拥有字面量枚举值时,它就带有了一种特殊的语义,即枚举成员成为了类型。<br />比如我们声明一个数字类型:

    enum Direction { Up, Down, Left, Right } const a = 0 console.log(a === Direction.Up) // true

    1. 我们把成员当做值使用,看来是没问题的,因为成员值本身就是0,那么我们再加几行代码:

    type c = 0 declare let b: c b = 1 // 不能将类型“1”分配给类型“0” b = Direction.Up // ok

    1. 我们看到,上面的结果显示这个枚举的成员居然也可以被当做类型使用,这就是枚举成员当做类型使用的情况。
    2. <a name="Zml2J"></a>
    3. ## 联合枚举类型
    4. 由于联合联合枚举,类型系统可以知道枚举里的值的集合。
    5. > 联合类型会在后面的章节提到

    enum Direction { Up, Down, Left, Right } declare let a: Direction enum Animal { Dog, Cat } a = Direction.Up // ok a = Animal.Dog // 不能将类型“Animal.Dog”分配给类型“Direction”

    1. 我们把 `a` 声明为 `Direction` 类型,可以看成我们声明了一个联合类型 `Direction.Up | Direction.Down | Direction.Left | Direction.Right`,只有这四个类型其中的成员才符合要求。
    2. <a name="YSjOt"></a>
    3. ## 枚举合并
    4. 我们可以分开声明枚举,他们会自动合并

    enum Direction { Up = ‘Up’, Down = ‘Down’, Left = ‘Left’, Right = ‘Right’ } enum Direction { Center = 1 }

    1. 编译为 JavaScript 后的代码如下:

    var Direction; (function (Direction) { Direction[“Up”] = “Up”; Direction[“Down”] = “Down”; Direction[“Left”] = “Left”; Direction[“Right”] = “Right”; })(Direction || (Direction = {})); (function (Direction) { Direction[Direction[“Center”] = 1] = “Center”; })(Direction || (Direction = {}));

    1. 因此上面的代码并不冲突。
    2. <a name="ByR2L"></a>
    3. ## 为枚举添加静态方法
    4. 借助 `namespace` 命名空间,我们甚至可以给枚举添加静态方法。<br />我们举个简单的例子,假设有十二个月份:

    enum Month { January, February, March, April, May, June, July, August, September, October, November, December, }

    1. 我们要编写一个静态方法,这个方法可以帮助我们把夏天的月份找出来:

    function isSummer(month: Month) { switch (month) { case Month.June: case Month.July: case Month.August: return true; default: return false } }

    1. 想要把两者结合就需要借助命名空间的力量了:

    namespace Month { export function isSummer(month: Month) { switch (month) { case Month.June: case Month.July: case Month.August: return true; default: return false } } } console.log(Month.isSummer(Month.January)) // false ```