做者:Marius Schulzhtml
譯者:前端小智前端
來源:mariusschulz.com/git
點贊再看,養成習慣
github
本文 GitHub:github.com/qq449245884… 上已經收錄,更多往期高贊文章的分類,也整理了不少個人文檔,和教程資料。歡迎Star和完善,你們面試能夠參照考點複習,但願咱們一塊兒有點東西。面試
我的專欄 ES6 深刻淺出已上線,深刻ES6 ,經過案例學習掌握 ES6 中新特性一些使用技巧及原理,持續更新中,←點擊可訂閱。typescript
系列文章以下:express
【TypeScript 演化史 -- 1】non-nullable 的類型app
【TypeScript 演化史 -- 2】基於控制流的類型分析 和 只讀屬性框架
【TypeScript 演化史 -- 3】標記聯合類型 與 never 類型async
【TypeScript 演化史 -- 4】更多的字面量類型 與 內置類型聲明
【TypeScript 演化史 -- 5】將 async/await 編譯到 ES3/ES5 (外部幫助庫)
【TypeScript 演化史 -- 6】對象擴展運算符和 rest 運算符及 keyof 和查找類型
【TypeScript 演化史 -- 7】映射類型和更好的字面量類型推斷
【TypeScript 演化史 -- 8】字面量類型擴展 和 無類型導入
【TypeScript 演化史 -- 9】object 類型 和 字符串索引簽名類型的點屬性
null
/undefined
在TypeScript 2.2中,空檢查獲得了進一步的改進。TypeScript 如今將帶有可空操做數的表達式標記爲編譯時錯誤。
具體來講,下面這些會被標記爲錯誤:
若是+
運算符的任何一個操做數是可空的,而且兩個操做數都不是any
或string
類型。
若是-
,*
,**
,/
,%
,<<
,>>
,>>>
, &
, |
或 ^
運算符的任何一個操做數是可空的。
若是 <
,>
,<=
,>=
或 in
運算符的任何一個操做數是可空的。
若是 instanceof
運算符的右操做數是可空的。
若是一元運算符+
,-
,~
,++
或者--
的操做數是可空的。
來看看若是我們不當心,可空表達式操做數就會坑下我們的狀況。在 TypeScript 2.2 以前,下面這個函數是能夠很好地編譯經過的:
function isValidPasswordLength(
password: string,
min: number,
max?: number
) {
return password.length >= min && password.length <= max;
}
複製代碼
注意,max
參數是可選的。這意味着我們可使用兩個或三個參數來調用isValidPasswordLength
:
isValidPasswordLength("open sesame", 6, 128); // true
isValidPasswordLength("open sesame", 6, 8); // false
複製代碼
密碼 "open sesame"
的長度爲10
個字符。所以,對於長度範圍 [6,128]
返回 true
,對於長度範圍[6,8]
返回false
,到目前爲止,一切 ok。
若是調用isValidPasswordLength
且不提供max
參數值,那麼當密碼長度超過 min
值時,我們可能但願返回 true
。然而,事實並不是如此:
isValidPasswordLength("open sesame", 6); // false
複製代碼
這裏的問題在於 <= max
比較。若是max
是 undefined
,那麼 <= max
的值永遠都爲false
。在這種狀況下,isValidPasswordLength
將永遠不會返回true
。
在 TypeScript 2.2 中,表達式password.length <= max
是不正確的類型,若是你的應用程序正在嚴格的null
檢查模式下運行:
function isValidPasswordLength(
password: string,
min: number,
max?: number
) {
return password.length >= min && password.length <= max; // Error: 對象可能爲「未定義」.
}
複製代碼
若是操做數的類型是
null
或undefined
或者包含null
或undefined
的聯合類型,則操做數視爲可空的。
注意:包含
null
或undefined
的聯合類型只會出如今--strictNullChecks
模式中,由於常規類型檢查模式下null
和undefined
在聯合類型中是不存在的。
那麼要怎麼修正這個問題呢?一種的解決方案是爲max
參數提供一個默認值,它只在傳遞undefined
時起做用。這樣,該參數仍然是可選的,但始終包含類型爲number
的值
function isValidPasswordLength(
password: string,
min: number,
max: number = Number.MAX_VALUE
) {
return password.length >= min && password.length <= max;
}
複製代碼
固然我們也能夠選擇其餘的方法,可是我以爲這個方法很好。只要再也不將max
與undefined
的值進行比較,就能夠了
TypeScript 的一個目的是支持不一樣框架和庫中使用的通用 JS 模式。從TypeScript 2.2開始,增長了對 ES6 混合類(mixin class)模式。接下來說講 mixin
是什麼,而後舉例說明了如何在 TypeScript 中使用它們。
混合類是實現不一樣功能方面的類。其餘類能夠包含 mixin
並訪問它的方法和屬性。這樣,mixin
提供了一種基於組合行爲的代碼重用形式。
混合類指一個extends
(擴展)了類型參數類型的表達式的類聲明或表達式. 如下規則對混合類聲明適用:
extends
表達式的類型參數類型必須是混合構造函數.any[]
的變長參數, 而且必須使用展開運算符在super(...args)
調用中將這些參數傳遞。定義完成以後,來研究一些代碼。下面是一個 Timestamped
函數,它在timestamp
屬性中跟蹤對象的建立日期:
type Constructor<T = {}> = new (..args: any[]) => T;
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now()
}
}
複製代碼
這看起來有點複雜,我們一行一行來看看:
type Constructor<T = {}> = new (..args: any[]) => T;
複製代碼
type Constructor <T>
是構造簽名的別名,該簽名描述了能夠構造通用類型T
的對象的類型,而且其構造函數接受任意數量的任何類型的參數。
接下來,讓咱們看一下mixin
函數自己:
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now();
};
}
複製代碼
Timestamped
函數接受一個名爲Base
的參數,該參數屬於泛型類型 TBase
。注意,TBase
必須與Constructor
兼容,即類型必須可以構造某些東西。
在函數體中,我們建立並返回一個派生自Base
的新類。這種語法乍一看可能有點奇怪。我們建立的是類表達式,而不是類聲明,後者是定義類的更經常使用方法。我們的新類定義了一個timestamp
的屬性,並當即分配自UNIX時代以來通過的毫秒數。
注意,從mixin
函數返回的類表達式是一個未命名的類表達式,由於class
關鍵字後面沒有名稱。與類聲明不一樣,類表達式沒必要命名。我們能夠選擇添加一個名稱,它將是類主體的本地名稱,並容許類引用本身
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class Timestamped extends Base {
timestamp = Date.now();
};
}
複製代碼
如今已經介紹了兩個類型別名和mixin
函數的聲明,接下來看看如何在另外一個類中使用 mixin
:
class User {
name: string;
constructor(name: string) {
this.name = name;
}
}
// 經過將`"Timestamped"`混合到"User"中建立一個新類
const TimestampedUser = Timestamped(User);
// 實例化新的 "TimestampedUser" 類
const user = new TimestampedUser("前端小智")
// 如今,我們能夠同時從User 類中訪問屬性
// 也能夠從 Timestamped 類中訪問屬性
console.log(user.name);
console.log(user.timestamp);
複製代碼
TypeScript 編譯器知道咱們在這裏建立並使用了一個mixin
,一切都是徹底靜態類型的,而且會自動完成和重構。
如今,看看一個稍微高級一點的 mixin
,類中定義一個構造函數
function Tagged<TBase extends Constructor>(Base: TBase) {
return class extends Base {
tag: string | null;
constructor(...args: any[]) {
super(...args);
this.tag = null;
}
};
}
複製代碼
若是在混合類中定義構造函數,那麼它必須有一個類型爲any[]
的rest
參數。這樣作的緣由是,mixin
不該該綁定到具備已知構造函數參數的特定類;所以,mixin
應該接受任意數量的任意值做爲構造函數參數。全部參數都傳遞給Base
的構造函數,而後mixin
執行它的任務。在我們的例子中,它初始化 tag
屬性。
混合構造函數類型指僅有單個構造函數簽名,且該簽名僅有一個類型爲
any[]
的變長參數,返回值爲對象類型. 好比, 有X
爲對象類型,new (...args: any[]) => X
是一個實例類型爲X
的混合構造函數類型。
之前面使用Timestamped
的相同方式來使用混合Tagged
:
// 經過 User 做爲混合 Tagged 來建立一個新類
const TaggedUser = Tagged(User);
// 實例化 "TaggedUser" 類
const user = new TaggedUser("John Doe");
// 如今,能夠從 User 類訪問屬性和 Tagged 中的屬性
user.name = "Jane Doe";
user.tag = "janedoe";
複製代碼
到目前爲止,我們只在mixin
中添加了數據屬性。如今來看看另外一個 mixin
,它額外實現了兩個方法:
fucntion Activatable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
isActivated = false;
activate() {
this.isActivated = true;
}
deactivate() {
this.isActivated = false;
}
}
}
複製代碼
我們從mixin
函數返回一個常規的類。這意味着我們可使用全部受支持的類功能,例如構造函數,屬性,方法,getter/setter
,靜態成員等。
如何所示,我們如何在 User
類中使用混合的 Activatable
:
const ActivatableUser = Activatable(User);
// 實例化新的"ActivatableUser"類
const user = new ActivatableUser("John Doe");
//初始化,isActivated 的值爲 false
console.log(user.isActivated);
user.activate();
console.log(user.isActivated); // true
複製代碼
組合的mixin,可讓它更加靈活。一個類能夠包含任意多的mixin
,爲了演示這點,我們把上面提到的全部mixin
代碼組合在一塊兒。
const SpecialUser = Activatable(Tagged(Timestamped(User)));
const user = new SpecialUser("John Doe");
複製代碼
固然 SpecialUser
類不必定很是有用,但關鍵是,TypeScript
靜態地理解這種mixin
組合。編譯器能夠類型檢查全部的使用,並在自動完成列表中建議可用的成員:
與類繼承進行對比,有個區別:一個類只能有一個基類。繼承多個基類在 JS 中不行的,所以在 TypeScript
中也不行。
原文: mariusschulz.com/blog/null-c… mariusschulz.com/blog/mixin-…
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。
我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!
關注公衆號,後臺回覆福利,便可看到福利,你懂的。