目录
一、解构
1、解构数组
2、对象解构
3、属性重命名
4、默认值
二、函数声明
三、展开
一、解构
1、解构数组
最简单的解构莫过于数组的解构赋值了:
let input = [1, 2];
let [first, second] = input;
console.log(first); // outputs 1
console.log(second); // outputs 2
这里创建两个命名变量first和second。相当于使用索引,更加方便:
first = input[0];
second = input[1];
解构作用于已声明的变量会更好:
// swap variables
[first, second] = [second, first];
作用于函数参数:
function f([first, second]: [number, number]) {
console.log(first);
console.log(second);
}
f(input);
如果数组元素比较多,可以使用...语法来概括多个元素:
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // outputs 1
console.log(rest); // outputs [ 2, 3, 4 ]
2、对象解构
对象也可以解构:
let o = {
a: "foo",
b: 12,
c: "bar"
};
let { a, b } = o;
这通过 o.a
and o.b
创建了 a
和 b
。 注意,如果你不需要 c
你可以忽略它。
就像数组解构,你可以用没有声明的赋值:
({ a, b } = { a: "baz", b: 101 });
同样也可以使用...语法:
let { a, ...passthrough } = o;
let total = passthrough.b + passthrough.c.length;
3、属性重命名
let { a: newName1, b: newName2 } = o;
这里的语法开始变得混乱。 你可以将 a: newName1
读做 "a
作为 newName1
"。 方向是从左到右,好像你写成了以下样子:
let newName1 = o.a;
let newName2 = o.b;
让人困惑的是,这里的冒号不是指示类型的。 如果你想指定它的类型, 仍然需要在其后写上完整的模式。
let {a, b}: {a: string, b: number} = o;
4、默认值
默认值可以让你在属性为undefined时使用缺省值:
function keepWholeObject(wholeObject: { a: string, b?: number }) {
let { a, b = 1001 } = wholeObject;
}
现在,即使 b
为 undefined , keepWholeObject
函数的变量 wholeObject
的属性 a
和 b
都会有值。
二、函数声明
解构也能用于函数的声明:
type C = { a: string, b?: number }
function f({ a, b }: C): void {
// ...
}
但是通常情况下,更多的是指定默认值,解构默认值有些棘手,首先你要在默认值之前设置格式。
function f({ a="", b=0 } = {}): void {
// ...
}
f();
其次,你需要知道在解构属性上给予一个默认或可选的属性用来替换主初始化列表。要知道c的定义有一个b可选 属性:
function f({ a, b = 0 } = { a: "" }): void {
// ...
}
f({ a: "yes" }); // ok, default b = 0
f(); // ok, default to {a: ""}, which then defaults b = 0
f({}); // error, 'a' is required if you supply an argument
要小心使用解构,从前面的例子看出,就算是最简单的解构表达式也很难理解,特别当存在深层嵌套解构的时候,就算这时没有堆叠在一起的重命名,默认值和类型注解,也是令人难以理解的,解构表达式要尽量保持小而简单。
三、展开
展开操作符与解构相反,它允许你讲一个数组展开为另一个数组,或将一个对象展开为另一个对象。例如:
let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second, 5];
这会令bothPlus
的值为[0, 1, 2, 3, 4, 5]
。 展开操作创建了 first
和second
的一份浅拷贝。 它们不会被展开操作所改变。
你还可以展开对象:
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, food: "rich" };
search
的值为{ food: "rich", price: "$$", ambiance: "noisy" }
。 对象的展开比数组的展开要复杂的多。 像数组展开一样,它是从左至右进行处理,但结果仍为对象。 这就意味着出现在展开对象后面的属性会覆盖前面的属性。 因此,如果我们修改上面的例子,在结尾处进行展开的话:
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { food: "rich", ...defaults };
那么,defaults
里的food
属性会重写food: "rich"
,在这里这并不是我们想要的结果。
对象的展开还有其他一些限制,首先,它仅包含对象自身的可枚举属性,也许你会丢失方法:
class C {
p = 12;
m() {
}
}
let c = new C();
let clone = { ...c };
clone.p; // ok
clone.m(); // error!
其次,TypeScript编译器不允许展开泛型函数的类型参数。