經過示例演示 TypeScript 的高級類型

原文地址: levelup.gitconnected.com/advanced-ty…
譯文地址:github.com/xiao-T/note…
本文版權歸原做者全部,翻譯僅用於學習。css


提高你對 TypeScript 的理解,並學這些高級的技術,能夠幫助你掌握該語言而且能夠更好的在 React 中使用 TypeScript。git

去年冬天,我開始使用 TypeScript,我已經從一個使用 any 的新手逐漸成長爲一個習慣使用高級內置類型和自定義類型的老手。經過在 JavaScript 代碼添加類型判斷,讓應用變得更加健壯。這篇文章提供了一些使用高級類型的示例,也展現瞭如何在 React 應用使用它們。github

在這裏,咱們將會探討 RecordPartialRequiredPick 和一個自定義Omit 類型。typescript

Record

Typescript 2.1 引入了一個很是有用的內置 type Record:它能夠建立類型 map,並且,很是適合建立複合型的 interface。爲了讓變量成爲 Record 類型,你須要傳入一個字符串做爲 key 和一些相關的 type。最簡單狀況是,你有一個 string 做爲值的類型。安全

const SERVICES: Record<string, string> = { 
    doorToDoor: "delivery at door",
    airDelivery: "flying in",
    specialDelivery: "special delivery",
    inStore: "in-store pickup",
};
複製代碼

這顯得微不足道,可是,它爲你平常編碼中提供更簡單的方式定義類型。一種常見的狀況是:當你須要把整個業務總體的 interface 做爲鍵值對保存在字典中時,Record 就很是有用。這個 model 能夠表示聯繫人、事件、用戶數據、交通請求、電影票據等,各類集合。在接下來的演示中,咱們爲 product 建立了一個 model,用戶能夠添加到購物車:編輯器

你會看到編輯器是如何自動幫咱們定義對象類型的,同時也會標記出錯誤提示,這是由於一些必要的屬性沒有定義:函數

img

另外,TypeScript 不容許咱們爲一些定義好的 type 建立空對象,須要提供相關的屬性,可是,這時 Record 就有用處了。工具

另外,也能夠用 string enum 做爲 Record 的 key。例如,咱們將會用 ErrorsEnum 來保存訪問相關的錯誤信息:學習

咱們來看看在 Material-UI 中它是如何加強類型的。就如指南中所說,你可使用 CSS-in-JS 添加自定義的樣式,而後,經過 withStyles HOC 注入。你能夠經過一個函數定義樣式,函數接受一個名 theme 的參數,而後,返回相關樣式的 className,還能夠爲這個函數定義類型:ui

你應該注意到,由於每一個樣式對象添加了 as CSSProperties,因此變得很是麻煩。另外,你就可使用 Record 帶來的好處:定一個帶有類型的 styles 函數:

如今,你能夠在任何組件中安全的使用它,而且會擺脫明肯定義 CSS properties 的束縛。

Partial and Required

Partial 可讓對象中全部的屬性變成可選的。在不少狀況它能夠幫到你,好比,當你須要數據渲染組件時,可是,你知道在組件 mount 時並不會加載數據:

你還也能夠用 Partial 來爲組件定義默認的 props。

相反,TypeScript v2.8 中引入的 Required ,可讓對象中的全部的屬性變成必選的:

Required 的用例之一就是 selectors:有時你想爲嵌套對象中的屬性創造選擇器,而且,你知道在選擇器調用時將會定義此屬性。你能夠爲此指定一個類型:

這看起來像是做弊,若是,你從可選屬性繼承必選屬性可能會引發類型錯誤,所以,要當心使用!

聽起來很傻,可是,若是你的代碼是自動生成的,而且,你全部的 interface 都是 Partial,UI 中全部的元素都是 Required,這種狀況並不稀奇。這時你須要檢查全部 undefined 的對象 😨。

Pick and Omit

曾經,你是否想過縮減一連串的類型,由於,你意識到下一個 class 並不須要這麼多屬性?或許你在重構時遇到這類問題,嘗試以一種全新的方式分佈系統的每一部分。這裏有幾種方式能夠解決這類問題。

Pick 可讓你在一個已經定義好的 interface 中挑選你須要的 key。

Omit 在 TypeScript 的 lib.d.ts 中並無預先定義,可是,它能夠很容易經過 PickExclude 來定義。它能夠從一個 interface 中排除掉你不想要的屬性。

下面兩張圖片中,ProductPhotoProps 會包含 Product 全部的屬性,name 和 descripition 除外:

其中一個實際示例,就來自個人項目:重構一個有着複雜依賴的龐大表單。有一個 FormProps 它包含了錯誤類型。從新思考後,對於第一個子組件這些錯誤類型並非必要的,可是,第二個組件仍然須要。除了錯誤類型,我用 Pick 提取了一些屬性構建了一個新的 interface,這種方式工做的很好。

固然,有多種方式能夠合併類型和定義它們之間的關係。若是,從一開始你就把很大一塊東西拆解成不少小塊,你或許解決了從對象中排除屬性的問題。可是,你會遇到擴展類型的問題。

繼承擴展 type/interface

當你須要擴展一個 interface 時,全部的屬性在新的 interface 都有效。咱們來看看,如何合併多個小的 interface 以便符合的咱們的任務需求:

這種方法並非很方便,由於,你必須更有預見性考慮你的對象。從另外一方面來說,它更加快速和簡單,讓你設計原型或者構建簡單 UI 更加炫酷,就像是把數據選到一個只讀塊中。

總結

經過一些真實代碼,我已經介紹了比較流行的 TypeScript 內置的類型。這只是一個 demo,可是,我知道這全部的類型至少會在一個真實場景中有效😉。我但願這篇文章能幫助到你,而且,鼓勵你不要懼怕 TypeScript。我用 React-Redux 建立了一個 SPA 的代碼倉,在這裏你能夠找到大多數的演示或者它們的替代方案。

然而,針對靜態類型我還想多說一些。一般,當你探索一種新技術或者面對一個開發中的挑戰時,你開始解決技術問題,反而忘記了你的目的。靜態類型並非你工做的目的,它只是一個工具。若是,它成爲了項目中的核心,這表明着你已經走偏了🚀。記住要在業務和技術之間作好平衡,編碼快樂!

相關文章
相關標籤/搜索