做者:Marius Schulz
譯者:前端小智
來源: https://mariusschulz.com/
點贊再看,養成習慣本文
GitHub
https://github.com/qq44924588... 上已經收錄,更多往期高贊文章的分類,也整理了不少個人文檔,和教程資料。歡迎Star和完善,你們面試能夠參照考點複習,但願咱們一塊兒有點東西。前端
TypeScript 2.4 爲標識符實現了拼寫糾正機制。即便我們稍微拼錯了一個變量、屬性或函數名,TypeScript 在不少狀況下均可以提示正確的拼寫。git
TypeScript 2.7 支持 ECMAScript 的數字分隔符提案。 這個特性容許用戶在數字之間使用下劃線(_
)來對數字分組(就像使用逗號和點來對數字分組那樣)。github
const worldPopulationIn2017 = 7_600_000_000; const leastSignificantByteMask = 0b1111_1111; const papayawhipColorHexCode = 0xFF_EF_D5;
數字分隔符不會改變數字字面量的值,但分組令人們更容易一眼就能讀懂數字。面試
這些分隔符對於二進制和十六進制一樣有用。typescript
let bits = 0b0010_1010; let routine = 0xC0FFEE_F00D_BED; let martin = 0xF0_1E_
注意,可能有些反常識,JS 裏的數字表示信用卡和電話號並不適當,這種狀況下使用字符串更好。微信
當我們將target
設置爲es2015
編譯的上述代碼時,TypeScript 將生成如下 JS 代碼:框架
const worldPopulationIn2017 = 7600000000; const leastSignificantByteMask = 255; const papayawhipColorHexCode = 16773077;
in
操做符細化和精確的 instanceofTypeScript 2.7帶來了兩處類型細化方面的改動 - 經過執行「類型保護」肯定更詳細類型的能力。dom
首先,instanceof
操做符如今利用繼承鏈而非依賴於結構兼容性, 能更準確地反映出 instanceof操做符在運行時的行爲。 這能夠幫助避免一些複雜的問題,當使用 instanceof去細化結構上類似(但無關)的類型時。ide
其次,in
操做符如今作爲類型保護使用,會細化掉沒有明確聲明的屬性名。函數
interface A { a: number }; interface B { b: string }; function foo(x: A | B) { if ("a" in x) { return x.a; } return x.b; }
在 JS 裏有一種模式,用戶會忽略掉一些屬性,稍後在使用的時候那些屬性的值爲 undefined
。
let foo = someTest ? { value: 42 } : {};
在之前TypeScript會查找 { value: number }
和 {}
的最佳超類型,結果是 {}
。 這從技術角度上講是正確的,但並非頗有用。
從2.7版本開始,TypeScript 會「規範化」每一個對象字面量類型記錄每一個屬性, 爲每一個 undefined
類型屬性插入一個可選屬性,並將它們聯合起來。
在上例中, foo
的最類型是 { value: number } | { value?: undefined }
。 結合了 TypeScript 的細化類型,這讓我們能夠編寫更具表達性的代碼且 TypeScript 也可理解。 看另一個例子:
// Has type // | { a: boolean, aData: number, b?: undefined } // | { b: boolean, bData: string, a?: undefined } let bar = Math.random() < 0.5 ? { a: true, aData: 100 } : { b: true, bData: "hello" }; if (bar.b) { // TypeScript now knows that 'bar' has the type // // '{ b: boolean, bData: string, a?: undefined }' // // so it knows that 'bData' is available. bar.bData.toLowerCase() }
這裏,TypeScript 能夠經過檢查 b
屬性來細化bar
的類型,而後容許咱們訪問 bData
屬性。
TypeScript 2.7 對ECMAScript裏的 symbols
有了更深刻的瞭解,你能夠更靈活地使用它們。
一個需求很大的用例是使用 symbols
來聲明一個類型良好的屬性。 好比,看下面的例子:
const Foo = Symbol("Foo"); const Bar = Symbol("Bar"); let x = { [Foo]: 100, [Bar]: "hello", }; let a = x[Foo]; // has type 'number' let b = x[Bar]; // has type 'string'
能夠看到,TypeScript 能夠追蹤到x
擁有使用符號 Foo
和 Bar
聲明的屬性,由於 Foo
和 Bar
被聲明成常量。 TypeScript 利用了這一點,讓 Foo
和 Bar
具備了一種新類型: unique symbols
。
unique symbols
是 symbols
的子類型,僅可經過調用 Symbol()
或 Symbol.for()
或由明確的類型註釋生成。 它們僅出如今常量聲明和只讀的靜態屬性上,而且爲了引用一個存在的 unique symbols
類型,你必須使用 typeof
操做符。 每一個對 unique symbols
的引用都意味着一個徹底惟一的聲明身份。
// Works declare const Foo: unique symbol; // Error! 'Bar' isn't a constant. let Bar: unique symbol = Symbol(); // Works - refers to a unique symbol, but its identity is tied to 'Foo'. let Baz: typeof Foo = Foo; // Also works. class C { static readonly StaticSymbol: unique symbol = Symbol(); }
由於每一個 unique symbols
都有個徹底獨立的身份,所以兩個 unique symbols
類型以前不能賦值和比較。
const Foo = Symbol(); const Bar = Symbol(); // Error: can't compare two unique symbols. if (Foo === Bar) { // ... }
另外一個可能的用例是使用 symbols作爲聯合標記。
// ./ShapeKind.ts export const Circle = Symbol("circle"); export const Square = Symbol("square"); // ./ShapeFun.ts import * as ShapeKind from "./ShapeKind"; interface Circle { kind: typeof ShapeKind.Circle; radius: number; } interface Square { kind: typeof ShapeKind.Square; sideLength: number; } function area(shape: Circle | Square) { if (shape.kind === ShapeKind.Circle) { // 'shape' has type 'Circle' return Math.PI * shape.radius ** 2; } // 'shape' has type 'Square' return shape.sideLength ** 2; }
TypeScript 2.7 引入了一個新的編譯器選項,用於類中嚴格的屬性初始化檢查。若是啓用了--strictPropertyInitialization
標誌,則類型檢查器將驗證類中聲明的每一個實例屬性
undefined
的類型--strictPropertyInitialization
選項是編譯器選項系列的一部分,當設置--strict
標誌時,該選項會自動啓用。 與全部其餘嚴格的編譯器選項同樣,我們能夠將--strict
設置爲true
,並經過將--strictPropertyInitialization
設置爲false
來有選擇地退出嚴格的屬性初始化檢查。
請注意,必須設置--strictNullCheck
標誌(經過—strict
直接或間接地設置),以便 --strictPropertyInitialization
起做用。
如今,來看看嚴格的屬性初始化檢查。若是沒有啓用--strictpropertyinitialized
標誌,下面的代碼類型檢查就能夠了,可是會在運行時產生一個TypeError
錯誤:
class User { username: string; } const user = new User(); // TypeError: Cannot read property 'toLowerCase' of undefined const username = user.username.toLowerCase();
出現運行時錯誤的緣由是,username
屬性值爲undefined
,由於沒有對該屬性的賦值。所以,對toLowerCase()
方法的調用失敗。
若是啓用——strictpropertyinitialize
,類型檢查器將會報一個錯誤:
class User { // Type error: Property 'username' has no initializer // and is not definitely assigned in the constructor username: string; }
接下來,看看四種不一樣的方法,能夠正確地輸入User
類來消除類型錯誤。
消除類型錯誤的一種方法是爲username
屬性提供一個包含undefined
的類型:
class User { username: string | undefined; } const user = new User();
如今,username
屬性保存undefined
的值是徹底有效的。可是,當我們想要將username
屬性用做字符串時,首先必須確保它實際包含的是字符串而不是undefined
的值,例如使用typeof
// OK const username = typeof user.username === "string" ? user.username.toLowerCase() : "n/a";
消除類型錯誤的另外一種方法是向username
屬性添加顯式初始化。經過這種方式,屬性將當即保存一個字符串值,而且不會明顯的undefined
:
class User { username = "n/a"; } const user = new User(); // OK const username = user.username.toLowerCase();
也許最有用的解決方案是將username
參數添加到構造函數中,而後將其分配給username
屬性。這樣,每當構造User
類的實例時,調用者必須提供用戶名做爲參數:
class User { username: string; constructor(username: string) { this.username = username; } } const user = new User("mariusschulz"); // OK const username = user.username.toLowerCase();
我們 還能夠經過刪除對類字段的顯式賦值並將public
修飾符添加到username
構造函數參數來簡化User
類,以下所示:
class User { constructor(public username: string) {} } const user = new User("mariusschulz"); // OK const username = user.username.toLowerCase();
請注意,嚴格的屬性初始化要求在構造函數中全部可能的代碼路徑中明確分配每一個屬性。 所以,如下代碼類型不正確,由於在某些狀況下,咱們將username
屬性賦值爲未初始化狀態:
class User { // Type error: Property 'username' has no initializer // and is not definitely assigned in the constructor. username: string; constructor(username: string) { if (Math.random() < 0.5) { this.username = username; } } }
若是類屬性既沒有顯式初始化,也沒有undefined
的類型,則類型檢查器要求直接在構造函數中初始化該屬性;不然,嚴格的屬性初始化檢查將失敗。若是我們但願在幫助方法中初始化屬性,或者讓依賴項注入框架來初始化屬性,那麼這是有問題的。在這些狀況下,我們必須將一個明確的賦值斷言(!
)添加到該屬性的聲明中:
class User { username!: string; constructor(username: string) { this.initialize(username); } private initialize(username: string) { this.username = username; } } const user = new User("mariusschulz"); // OK const username = user.username.toLowerCase();
經過向username
屬性添加一個明確的賦值斷言,這會告訴類型檢查器,指望對username
屬性進行初始化,即便它本身沒法檢測到這一點。如今我們的責任是確保在構造函數返回後明確地將屬性賦值給它,因此必須當心;不然,username
屬性可能被明顯的undefined
或者在運行時就會報 TypeError
錯誤。
儘管我們嘗試將類型系統作的更富表現力,但咱們知道有時用戶比TypeScript
更加了解類型。
上面提到過,顯式賦值斷言是一個新語法,使用它來告訴 TypeScript 一個屬性會被明確地賦值。 可是除了在類屬性上使用它以外,在TypeScript 2.7
裏你還能夠在變量聲明上使用它!
let x!: number[]; initialize(); x.push(4); function initialize() { x = [0, 1, 2, 3]; }
假設咱們沒有在 x
後面加上感嘆號,那麼TypeScript會報告 x
從未被初始化過。 它在延遲初始化或從新初始化的場景下很方便使用。
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
原文:
http://mariusschulz.com/blog/...
https://mariusschulz.com/blog...
文章每週持續更新,能夠微信搜索「 大遷世界 」第一時間閱讀和催更(比博客早一到兩篇喲),本文 GitHub https://github.com/qq449245884/xiaozhi 已經收錄,整理了不少個人文檔,歡迎Star和完善,你們面試能夠參照考點複習,另外關注公衆號,後臺回覆福利,便可看到福利,你懂的。