1. 程式人生 > 程式設計 >詳解TS物件擴充套件運算子和rest運算子

詳解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 中編寫這個函式呢?先嚐試一下:

詳解TS物件擴充套件運算子和rest運算子

有了這兩個型別註釋,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物件上不存在的鍵會發生什麼

詳解TS物件擴充套件運算子和rest運算子

編譯器會報錯,這很好,它阻止咱們試圖讀取一個不存在的屬性。

另一個真實的示例,請檢視與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運算子的資料請關注我們其它相關文章!