詳解TS物件擴充套件運算子和rest運算子
概述
TypeScript 2.1 增加了對 物件擴充套件運算和 rest 屬性提案的支援,該提案在 ES2018 中標準化。可以以型別安全的方式使用 rest 和 spread 屬性。
物件 rest 屬性
假設已經定義了一個具有三個屬性的簡單字面量物件
const marius = {
name: "Marius Schulz",website: "https://mariusschulz.com/",twitterHandle: "@mariuss程式設計客棧chulz"
};
使用 ES6 解構語法,可以建立幾個區域性變數來儲存相應屬性的值。TypeScript 將正確地推斷每個變數的型別:
const { name,website,twitterHandle } = marius; name; // Type string website; // Type string twitterHandle; // Type string
這些都是正確的,但這到現在也啥新鮮的。除了提取感興趣的一組屬性之外,還可以使用...語法將所有剩餘的屬性收集到rest元素中:
const { twitterHandle,...rest } = marius; twitterHandle; // Type string rest; // Type { name: string; website: string; }
TypeScript 會為得到結果的區域性變數確定正確的型別。雖然twitterHandle變數是一個普通的字串,但rest變數是一個物件,其中包含剩餘兩個未被解構的屬性。
物件擴充套件屬性
假設咱們希望使用fetch()API 發出 HTTP 請求。它接受兩個引數:一個URL和一個options物件,options包含請求的任何自定義設定。
在應用程式中,可以封裝對fetch()的呼叫,並提供預設選項和覆蓋給定請求的特定設定。這些配置項類似如下:
const defaultOptions = { method: "GET",credentials: "same-origin" }; const requestOptions = { method: "POST",redirect: "follow" };
使用物件擴充套件,可以將兩個物件合併成一個新物件,然後傳遞給fetch()方法
// Type { method: string; redirect: string; credentials: string; } const options = { ...defaultOptions,...requestOptions };
物件擴充套件屬性建立一個新物件,複製defaultOptions中的所有屬性值,然後按照從左到右的順序複製requestOptions中的所有屬性值,最後得到的結果如下:
console.log(options); // { // method: "POST",// credentials: "same-origin",// redirect: "follow" // }
請注意,分配順序很重要。如果一個屬性同時出現在兩個物件中,則後分配的會替換前面程式設計客棧的。
當然,TypeScript 理解這種順序。因此,如果多個擴充套件物件使用相同的鍵定義一個屬性,那麼結果物件中該屬性的型別將是最後一次賦值的屬性型別,因為它覆蓋了先前賦值的屬性:
const obj1 = { prop: 42 }; const obj2 = { prop: "Hello World" }; const result1 = { ...obj1,...obj2 }; // Type { prop: string } const result2 = { ...obj2,...obj1 }; // Type { prop: number }
製作物件的淺拷貝
物件擴充套件可用於建立物件的淺拷貝。假設咱希望通過建立一個新物件並複製所有屬性來從現有todo項建立一個新todo項,使用物件就可以輕鬆做到:
const todo = { text: "Water the flowers",completed: false,tags: ["garden"] }; const shallowCopy = { ...todo };
實際上,你會得到一個新物件,所有的屬性值都被複制:
console.log(todo === shallowCopy); // false console.log(shallowCopy); // { // text: "Water the flowers",// completed: false,// tags: ["garden"] // }
現在可以修改text屬性,但不會修改原始的todo項:
hallowCopy.text = "Mow the lawn"; console.log(shallowCopy); // { // text: "Mow the lawn",// tags: ["garden"] // } console.log(todo); // { // text: "Water the flowers",// tags: ["garden"] // }
但是,新的todo項引用與第一個相同的tags陣列。由於是淺拷貝,改變陣列將影響這兩個todo
shallowCopy.tags.push("weekend");
console.log(shallowCopy);
// {
// text: "Mow the lawn",// tags: ["garden","weekend"]
// }
console.log(todo);
// {
// text: GvUDK"Water the flowers","weekend"]
// }
如果想建立一個序列化物件的深拷貝,可以考慮使用jsON.parse(jsON.stringify(obj))或其他方法,如object.assign()。物件擴充套件僅拷貝屬性值,如果一個值是對另一個物件的引用,則可能導致意外的行為。
keyof 和查詢型別
JS 是一種高度動態的語言。在靜態型別系統中捕獲某些操作的語義有時會很棘手。以一個簡單的prop函式為例:
function prop(obj,key) { return obj[key]; }
它接受一個物件和一個鍵,並返回相應屬性的值。一個物件的不同屬性可以有完全不同的型別,咱們甚至不知道obj是什麼樣子的。
那麼如何在 TypeScript 中編寫這個函式呢?先嚐試一下:
有了這兩個型別註釋,obj必須是物件,key必須是字串。咱們現在已經限制了兩個引數的可能值集。然而,TS 仍然推斷返回型別為any:
const todo = { id: 1,text: "Buy milk",due: new Date(2016,11,31) }; const id = prop(todo,"id"); // any const text = prop(todo,"text"); // any const due = prop(todo,"due"); // any
如果沒有更進一步的資訊,TypeScript 就不知道將為key引數傳遞哪個值,所以它不能推斷出prop函式的更具體的返回型別。咱們需要提供更多的型別資訊來實現這一點。
keyof 操作符號
在 JS 中屬性名稱作為引數的 API 是相當普遍的,但是到目前為止還沒有表達在那些 API 中出現的型別關係。
TypeScript 2.1 新增加keyof操作符。輸入索引型別查詢或keyof,索引GvUDK型別查詢keyof T產生的型別是T的屬性名稱。假設咱們已經定義了以下Todo介面:
interface Todo { id: number; text: string; due: Date; }
各位可以將keyof操作符應用於Todo型別,以獲得其所有屬性鍵的型別,該型別是字串字面量型別的聯合
type TodoKeys = keyof Todo; // "id" | "text" | "due"
當然,各位也可以手動寫出聯合型別"id" | "text" | "due",而不是使用keyof程式設計客棧,但是這樣做很麻煩,容易出錯,而且維護起來很麻煩。而且,它應該是特定於Todo型別的解決方案,而不是通用的解決方案。
索引型別查詢
有了keyof,咱們現在可以改進prop函式的型別註解。我們不再希望接受任意字串作為key引數。相反,咱們要求引數key實際存在於傳入的物件的型別上
function prop <T,K extends keyof T>(obj: T,key: K) { return obj[key] }
TypeScript 現在以推斷prop函式的返回型別為T[K],這個就是所謂的索引型別查詢或查詢型別。它表示型別T的屬性K的型別。如果現在通過prop方法訪問下面todo的三個屬性,那麼每個屬性都有正確的型別:
const todo = { id: 1,"id"); // number const text = prop(todo,"text"); // string const due = prop(todo,"due"); // Date
現在,如果傳遞一個todo物件上不存在的鍵會發生什麼
編譯器會報錯,這很好,它阻止咱們試圖讀取一個不存在的屬性。
另一個真實的示例,請檢視與TypeScript編譯器一起釋出的lib.es2017.object.d.ts型別宣告檔案中Object.entries()方法:
interface ObjectConstructor { // ... entries<T extends { [key: string]: any },K extends keyof T>(o: T): [keyof T,T[K]][]; // ... }
entries方法返回一個元組陣列,每個元組包含一個屬性鍵和相應的值。不可否認,在返回型別中有大量的方括號,但是我們一直在尋找型別安全性。
以上就是詳解TS物件擴充套件運算子和rest運算子的詳細內容,更多關於TS物件擴充套件運算子和rest運算子的資料請關注我們其它相關文章!