⒈解構數組
最簡單的解構莫過於數組的解構賦值了:
let input = [1, 2]; let [first, second] = input; console.log(first); // outputs 1 console.log(second); // outputs 2
這創建了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 ]
當然,由於是JavaScript, 你可以忽略你不關心的尾隨元素:
let [first] = [1, 2, 3, 4]; console.log(first); // outputs 1
或其它元素:
let [, second, , fourth] = [1, 2, 3, 4];
⒉對象解構
你也可以解構對象:
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 });
注意,我們需要用括號將它括起來,因為Javascript通常會將以 {
起始的語句解析為一個塊。
你可以在對象里使用...
語法創建剩余變量:
let { a, ...passthrough } = o;
let total = passthrough.b + passthrough.c.length;
⒊屬性重命名
你也可以給屬性以不同的名字:
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;
⒋默認值
默認值可以讓你在屬性為 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 { // ... }
但是,通常情況下更多的是指定默認值,解構默認值有些棘手。 首先,你需要在默認值之前設置其格式。
其次,你需要知道在解構屬性上給予一個默認或可選的屬性用來替換主初始化列表。 要知道 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編譯器不允許展開泛型函數上的類型參數。 這個特性會在TypeScript的未來版本中考慮實現。