解构

Another TypeScript已经可以解析其它 ECMAScript 2015 特性了。 完整列表请参见
。 本章,我们将给出一个简短的概述。

数组解构

  1. let a, b, rest;
  2. [a, b] = [10, 20];
  3. console.log(a); // 10
  4. console.log(b); // 20
  5. // ...用剩余变量创建一个新数组
  6. [a, b, ...rest] = [10, 20, 30, 40, 50];
  7. console.log(a); // 10
  8. console.log(b); // 20
  9. console.log(rest); // [30, 40, 50]
  10. // 语法糖,等同于let { a, b } = { a: 10, b: 20 }
  11. ({ a, b } = { a: 10, b: 20 });
  12. console.log(a); // 10
  13. console.log(b); // 20
  14. // Stage 4(finished) proposal
  15. ({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});
  16. console.log(a); // 10
  17. console.log(b); // 20
  18. console.log(rest); // {c: 30, d: 40}

解构作用于已声明的变量会更好:

  1. // 交换两个变量的值
  2. [first, second] = [second, first];
  3. // 作用于函数的参数
  4. function f([first, second]: [number, number]) {
  5. console.log(first);
  6. console.log(second);
  7. }
  8. f(input);

当然,由于是JavaScript, 你可以忽略你不需要的元素:

  1. let [first] = [1, 2, 3, 4];
  2. console.log(first); // outputs 1
  3. let [, second, , fourth] = [1, 2, 3, 4];
  4. console.log(second,fourth) // 2 4

对象解构

  1. let o = {
  2. a: "foo",
  3. b: 12,
  4. c: "bar"
  5. };
  6. let { a, b } = o;

就像数组解构,你可以用没有声明的赋值:

  1. ({ a, b } = { a: "baz", b: 101 });
  2. // 语法糖 等同于 let { a, b } = { a: "baz", b: 101 }

注意,我们需要用括号将它括起来,因为Javascript通常会将以 { 起始的语句解析为一个块。
你也可以在对象里使用 … 语法创建剩余变量:

  1. let o = {
  2. a: "foo",
  3. b: 12,
  4. c: "bar"
  5. };
  6. let { a, ...passthrough } = o; // a = "foo",passthrough = { b: 12, c: "bar" }

属性重命名

你也可以给属性以不同的名字:

  1. let { a: newName1, b: newName2 } = o;

这里的语法开始变得混乱。 你可以将 a: newName1 读做 “a 作为 newName1”。 方向是从左到右,好像你写成了以下样子:

  1. let newName1 = o.a;
  2. let newName2 = o.b;

令人繁琐的是,这里的冒号不是指示类型的。 如果你想指定它的类型, 仍然需要在其后写上完整的模式。

  1. let {a, b}: {a: string, b: number} = o;

默认值

像Python一样,你可以给函数的参数设置默认值,这样当该参数的值为null或undefined的时候,就会使用默认值。

  1. function keepWholeObject(wholeObject: { a: string, b?: number }) {
  2. let { a, b = 1001 } = wholeObject;
  3. }
  4. function go(href = "https://localhost") {
  5. console.log(href)
  6. }

不难发现,重命名,默认值和类型注解所用的都是 : 号表示,所以在函数中即使是简单的解构,阅读起来也是非常复杂的,谨慎使用。

带有解构的函数声明 (很tm复杂)

解构也能用于函数声明。 看以下简单的情况:

  1. type C = { a: string, b?: number }
  2. function f({ a, b }: C): void {
  3. // ...
  4. }

但是,通常情况下更多的是指定默认值,解构默认值有些棘手。 首先,你需要在默认值之前设置其格式。

  1. function f({ a = "a", b = 0 } = {}): void {
  2. // ...
  3. console.log(a);
  4. console.log(b):
  5. }
  6. // 其中{ a="a", b = 0 } 表示对传入的对象进行解构,从中拿到属性 a 和 b ,如果a和b都不存在,则设置为默认值 "a" 和 0, 比如传入一个对象{a:"hello",b:10},则最后输出的就是hello和10
  7. // = {} 表示如果没有参数传进来,则将整个参数的默认值设为空对象 {},但由于a和b都设置了默认值,所以最后还是用{ a: "a", b: 0 }作为最后的参数
  8. f() // a 0

上面的代码使用了类型推断,将在本手册后文介绍。

如果,在参数解构后,你还可以设置一个默认值,如下中 { a, b = 0 } = { a: “” } 一样,如果传入的对象中没有 a这个属性,则最终会把最后的默认值 { a: “” } 作为a的值。注意:参数最终的默认值会和参数内的默认值进行合并,并且同名的类型会被参数默认值覆盖,如果没有同名则不会覆盖。
比如下例中:我们在参数内给b设置了默认值,而参数默认值中没有设置,则最后会将 { b: 0 } 和 { a: “” } 进行合并,得到最终的默认值就是 { a: “”, b: 0 },就是这样。

  1. function f({ a, b = 0 } = { a: "" }): void {
  2. // ...
  3. console.log(a);
  4. console.log(b):
  5. }
  6. f({ a: "yes" }); // b默认为0,输出 yes 0
  7. f(); // 允许,在参数为空时,有一个默认值 {a:""},同时b会被初始化为0,最终参数为{a:"",b:0}
  8. f({}); // 错误!,因为a没有在参数列表中设置默认值,所以a是必选项,ts中编辑器报错。

要小心使用解构。 从前面的例子可以看出,就算是最简单的解构表达式也是难以理解的。 尤其当存在深层嵌套解构的时候,就算这时没有堆叠在一起的重命名,默认值和类型注解,也是令人难以理解的。 解构表达式要尽量保持小而简单。 你自己也可以直接使用解构将会生成的赋值表达式。

展开

也就是 … 语法,它允许你将一个数组展开为另一个数组,或将一个对象展开为另一个对象。 例如:

  1. let first = [1, 2];
  2. let second = [3, 4];
  3. let bothPlus = [0, ...first, ...second, 5]; // [0, 1, 2, 3, 4, 5]

展开操作创建了 first和second的一份浅拷贝。 它们不会被展开操作所改变。
展开对象:

  1. let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
  2. let search = { ...defaults, food: "rich" };

像数组展开一样,它是从左至右进行处理,但结果仍为对象。 这就意味着出现在展开对象后面的属性会覆盖前面的属性。 也就是说,上例中的 food: “rich” 将会覆盖展开中的 food: “spicy”。因此,要注意展开的位置和时机。
对象展开还有其它一些意想不到的限制。 首先,它仅包含对象 自身的可枚举属性。 简单来说,就是只能展开复制基本类型和复杂类型数据,而不包括函数和方法。

  1. class C {
  2. p = 12;
  3. m() {}
  4. }
  5. let c = new C();
  6. let clone = { ...c };
  7. clone.p;
  8. clone.m(); // 错误,展开会去除方法和函数