字面量类型和枚举

652 阅读3分钟

字面量类型

它代表着比原始类型更精确的类型,同时也是原始类型的子类型

interface Tmp {
    bool: true | false;
    num: 1 | 2 | 3;
    str: "a" | "b" | "c"
  }

联合类型

而联合类型你可以理解为,它代表了一组类型的可用集合,只要最终赋值的类型属于联合类型的成员之一,就可以认为符合这个联合类型

interface Tmp2 {
    mixed: true | string | 599 | {} | (() => {}) | (1 | 2)
  }
  • 对于联合类型中的函数类型,需要使用括号()包裹起来
  • 函数类型并不存在字面量类型,因此这里的 (() => {}) 就是一个合法的函数类型
  • 你可以在联合类型中进一步嵌套联合类型,但这些嵌套的联合类型最终都会被展平到第一级中

联合类型的常用场景

通过多个对象类型的联合,来实现手动的互斥属性,即这一属性如果有字段1,那就没有字段2

interface Tmp3 {
 user:
   | {
       vip: true;
       expires: string;
     }
   | {
       vip: false;
       promotion: string;
     };
}

declare var tmp: Tmp3;

if (tmp.user.vip) {//根据vip判断
 console.log(tmp.user.expires);
}

对象字面量类型

interface Tmp4 {
 obj: {
   name: "linbudu",
   age: 18
 }
}

const tmp4: Tmp4 = {
 obj: {
   name: "linbudu",
   age: 18
 }
}

无论是原始类型还是对象类型的字面量类型,它们的本质都是类型而不是值

枚举

常量被真正地约束在一个命名空间下

如果没有声明枚举的值,它会默认使用数字枚举,并且从 0 开始,以 1 递增

enum Items {
  // 0 
  Foo,
  //1
  Bar,
  // 2
  Baz
}

如果你只为某一个成员指定了枚举值,那么之前未赋值成员仍然会使用从 0 递增的方式,之后的成员则会开始从枚举值递增

enum Items {
  // 0 
  Foo,
  Bar = 599,
  // 600
  Baz
}

在数字型枚举中,你可以使用延迟求值的枚举值,比如函数

但要注意,延迟求值的枚举值是有条件的。如果你使用了延迟求值,那么没有使用延迟求值的枚举成员必须放在使用常量枚举值声明的成员之后(如上例),或者放在第一位

const returnNum = () => 599;

enum Items1 {
Foo = returnNum(),
Bar = 599,
Baz
}

枚举和对象的重要差异

对象是单向映射的,我们只能从键映射到键值。而枚举是双向映射的,即你可以从枚举成员映射到枚举值,也可以从枚举值映射到枚举成员

enum Items2 {
  Foo,
  Bar,
  Baz
}

const fooValue = Items.Foo; // 0
const fooKey = Items[0]; // "Foo"
要了解这一现象的本质,我们需要来看一看枚举的编译产物,如以上的枚举会被编译为以下 JavaScript 代码
(function (Items) {
    Items[Items["Foo"] = 0] = "Foo";
    Items[Items["Bar"] = 1] = "Bar";
    Items[Items["Baz"] = 2] = "Baz";
})(Items || (Items = {}));

obj[k] = v 的返回值即是 v,因此这里的 obj[obj[k] = v] = k 本质上就是进行了 obj[k] = v 与 obj[v] = k 这样两次赋值

但需要注意的是,仅有值为数字的枚举成员才能够进行这样的双向枚举,字符串枚举成员仍然只会进行单次映射:

enum Items {
  Foo,
  Bar = "BarValue",
  Baz = "BazValue"
}

// 编译结果,只会进行 键-值 的单向映射
"use strict";
var Items;
(function (Items) {
    Items[Items["Foo"] = 0] = "Foo";
    Items["Bar"] = "BarValue";
    Items["Baz"] = "BazValue";
})(Items || (Items = {}));

常量枚举

它和普通枚举的差异主要在访问性与编译产物。对于常量枚举,你只能通过枚举成员访问枚举值(而不能通过值访问成员)

const enum Items3 {
    Foo,
    Bar,
    Baz
  }
  const fooValue1 = Items.Foo; // 0