枚举、元祖、类型操作

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-05-21

枚举

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

会被编译为:

"use strict";
var Days;
(function (Days) {
  Days[Days["Sun"] = 0] = "Sun";
  Days[Days["Mon"] = 1] = "Mon";
  Days[Days["Tue"] = 2] = "Tue";
  Days[Days["Wed"] = 3] = "Wed";
  Days[Days["Thu"] = 4] = "Thu";
  Days[Days["Fri"] = 5] = "Fri";
  Days[Days["Sat"] = 6] = "Sat";
})(Days || (Days = {}));

枚举成员会被赋值为从0开始递增的数字,同时也会对枚举值到枚举名进行反向映射。

console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true

console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true

手动赋值

未手动赋值的枚举项会接着上一个枚举项递增。

enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true 自动递增
console.log(Days["Sat"] === 6); // true 自动递增

如果未手动赋值的枚举项与手动赋值的重复了,TypeScript 是无法察觉的。

enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 3); // true
console.log(Days["Wed"] === 3); // true
console.log(Days[3] === "Sun"); // false
console.log(Days[3] === "Wed"); // true 

因为会被编译为如下结果:

"use strict";
var Days;
(function (Days) {
  Days[Days["Sun"] = 3] = "Sun";
  Days[Days["Mon"] = 1] = "Mon";
  Days[Days["Tue"] = 2] = "Tue";
  Days[Days["Wed"] = 3] = "Wed"; // Days[3] = "Wed" 覆盖了 Days[3] = "Sun"
  Days[Days["Thu"] = 4] = "Thu";
  Days[Days["Fri"] = 5] = "Fri";
  Days[Days["Sat"] = 6] = "Sat";
})(Days || (Days = {}));

手动赋值的枚举项也可以是小数或负数,此时后续没手动赋值的项的递增步长仍为1。

手动赋值的枚举项也可以不是数字,但是无法进行反向映射了。

enum Days {Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat = "S"};

// 最后Sat会被编译为 Days["Sat"] = "S"; 也就无法进行反向映射

常数项和计算所得项

此前提到都是常数项,一个典型的计算所得项的例子:

enum Days {
  Green,
  Blue = "blue".length,
  // Red, // error 如果紧接在计算所得项后面的是未手动赋值的项,那么它就会因为无法获得初始值而报错
};

编译为:

(function (Days) {
  Days[Days["Green"] = 0] = "Green";
  Days[Days["Blue"] = "blue".length] = "Blue";
})(Days || (Days = {}));

常数枚举

常数枚举是指使用 const enum 定义的枚举,会在编译阶段被删除,代码中使用到的,会在编译阶段直接用枚举值替换。

const enum Directions {
  Up,
  Down,
  Left,
  Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

会被编译为:

"use strict";
let directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];

并且常数枚举不能包含计算成员,否则会报错。

外部枚举

外部枚举 (Ambient Enums) 是使用 (declare enum) 定义的枚举类型,常出现在声明文件中,在编译结果会被删除。

declare enum Days {Sun, Mon}

declare const enum Directions {
  Up,
  Down,
  Left,
  Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

var a = 1;

编译结果只有 var a = 1,也不会有 let directions 这一行。

元组

数组合并了相同类型的对象,而元祖 (Tuple) 合并了不同类型的对象。

let tom: [string, number] = ['Tom', 24];

越界的元素

当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型。

let tom: [string, number];
tom = ['Tom', 24];
tom.push('Jack')
tom.push(25)
// tom.push(true) // error 类型“boolean”的参数不能赋给类型“string | number”的参数。

类型别名

类型别名用来给一个类型起一个新名字;

type Name = string;
type NameResolver = () => string;

type NameOrResolver = Name | NameResolver;

function getName(n: NameOrResolver): Name {
  if (typeof n === 'string') {
    return n;
  }
  return n();
}

字符串字面量类型

字符串字面量类型用来约束取值只能取某几个字符串中的一个。与类型别名一样使用type 定义。

type E  = 'click' | 'scroll' | 'mousemove';

function handleEvent(ele: Element, event: E) {
  // do something
}

handleEvent(document.querySelector('.a') as Element, 'click')
// handleEvent(document.querySelector('.a') as Element, 'dbclick') 
// error 类型“"dbclick"”的参数不能赋给类型“E”的参数。

keyof

keyof,后面接类型,从一个类型中取出所有的key组成一个字符串字面量类型,即结果是字符串字面量类型

interface Person {
  name: string;
  age: number;
}

type PersonKey = keyof Person;

function getValue(p: Person, key: PersonKey): string | number {
  return p[key];
}

let a : Person = {
  name: 'jack',
  age: 20
}

getValue(a, 'age')
getValue(a, 'name')

// getValue(a, 'sex') // 类型“"sex"”的参数不能赋给类型“keyof Person”的参数。

多在泛型中使用

function getVal<T>(obj: T, key: keyof T): T[keyof T] {
  return obj[key]
}

getVal(a, 'age')
getVal(a, 'name')
// getVal(a, 'sex') // 类型“"sex"”的参数不能赋给类型“keyof Person”的参数。

typeof

typeof 后面接值,取出一个值的类型,即结果是类型。

var a = {
  name: 'jack',
  age: 24,
}

var b: typeof a = {
  name: 'tom',
  age: 18
  // sex: 'males' 不能将类型“{ name: string; age: number; sex: string; }”分配给类型“{ name: string; age: number; }”
}

// a 和 b 的类型,都是
// {
//   name: string;
//   age: number;
// }