做者:Marius Schulz
譯者:前端小智
來源: https://mariusschulz.com/
點贊再看,養成習慣本文
GitHub
https://github.com/qq44924588... 上已經收錄,更多往期高贊文章的分類,也整理了不少個人文檔,和教程資料。歡迎Star和完善,你們面試能夠參照考點複習,但願咱們一塊兒有點東西。html
TypeScript 2.4 實現了最受歡迎的特性之一:字符串枚舉,或者更精確地說,帶有字符串值成員的枚舉。前端
如今能夠將字符串值分配給枚舉成員了:git
enum MediaTypes { JSON = 'application/json', XML = 'application/xml' }
字符串枚舉能夠像 TypeScript 中的任何其餘枚舉同樣使用:github
enum MediaTypes { JSON = "application/json", XML = "application/xml" } fetch("https://example.com/api/endpoint", { headers: { Accept: MediaTypes.JSON } }).then(response => { // ... });
下面編譯目標爲 es3/es5
生成的 JS 的代碼:面試
var MediaTypes; (function (MediaTypes) { MediaTypes["JSON"] = "application/json"; MediaTypes["XML"] = "application/xml"; })(MediaTypes || (MediaTypes = {})); fetch("https://example.com/api/endpoint", { headers: { Accept: MediaTypes.JSON } }).then(function (response) { // ... });
這個輸出幾乎與編譯器爲帶有數字成員的枚舉生成的輸出相似,只是字符串值成員沒有反向映射。typescript
TypeScript 爲每一個構造映射對象的枚舉發出一些映射代碼。對於字符串值枚舉成員,此映射對象定義從鍵到值的映射,反之則不是:json
var MediaTypes; (function (MediaTypes) { MediaTypes["JSON"] = "application/json"; MediaTypes["XML"] = "application/xml"; })(MediaTypes || (MediaTypes = {}));
這意味着我們能夠經過鍵解析一個值,但不能經過鍵的值解析一個鍵segmentfault
MediaTypes["JSON"]; // "application/json" MediaTypes["application/json"]; // undefined MediaTypes["XML"]; // "application/xml" MediaTypes["application/xml"]; // undefined
與具備數字值成員的枚舉進行比較:api
enum DefaultPorts { HTTP = 80, HTTPS = 443 }
在這種狀況下,編譯器還會生成從值到鍵的反向映射app
var DefaultPorts; (function(DefaultPorts) { DefaultPorts[(DefaultPorts["HTTP"] = 80)] = "HTTP"; DefaultPorts[(DefaultPorts["HTTPS"] = 443)] = "HTTPS"; })(DefaultPorts || (DefaultPorts = {}));
這種反向映射容許經過鍵值解析鍵和經過鍵解析值
DefaultPorts["HTTP"]; // 80 DefaultPorts[80]; // "HTTP" DefaultPorts["HTTPS"]; // 443 DefaultPorts[443]; // "HTTPS"
爲了不生成的枚舉映射代碼的開銷,我們能夠經過將const
修飾符添加到聲明中,將MediaTypes
枚舉轉換爲const
枚舉:
const enum MediaTypes { JSON = "application/json", XML = "application/xml" } fetch("https://example.com/api/endpoint", { headers: { Accept: MediaTypes.JSON } }).then(response => { // ... });
使用const
修飾符後,編譯器將不會爲MediaTypes
枚舉生成任何映射代碼。相反,它將內聯全部使用站點上每一個枚舉成員的值,從而可能節省一些字節和屬性訪問間接性的開銷:
fetch("https://example.com/api/endpoint", { headers: { Accept: "application/json" /* JSON */ } }).then(function (response) { // ... });
可是,若是因爲某種緣由,我們須要在運行時訪問映射對象,該怎麼辦呢
preserveConstEnums
生成一個常量枚舉有時,可能有必要發出一個const枚舉的映射代碼,例如,當某些 JS 代碼須要訪問它時,在這種狀況下,能夠在tsconfig.json
文件中打開prepareConstEnums
編譯器選項:
{ "compilerOptions": { "target": "es5", "preserveConstEnums": true } }
若是我們使用設置的preserveConstEnums
選項再次編譯代碼,編譯器仍然會內聯MediaTypes
,同時它也會發出映射代碼:
var MediaTypes; (function (MediaTypes) { MediaTypes["JSON"] = "application/json"; MediaTypes["XML"] = "application/xml"; })(MediaTypes || (MediaTypes = {})); fetch("https://example.com/api/endpoint", { headers: { Accept: "application/json" /* JSON */ } }).then(function (response) { // ... });
TypeScript 2.4 引入了弱類型的概念。若是類型的全部屬性都是可選的,則認爲類型是弱類型。更具體地說,弱類型定義一個或多個可選屬性,沒有必需屬性,也沒有索引簽名。
例如,下面的類型被認爲是弱類型:
interface PrettierConfig { printWidth ?: number; tabWidth?: number; semi?: boolean; }
弱類型檢測的主要目標是發現代碼中可能的錯誤,不然這些錯誤將是無聲的錯誤。考慮一下這個例子:
interface PrettierConfig { printWidth?: number; tabWidth?: number; semi?: boolean; } function createFormatter(config: PrettierConfig) { // ... } const prettierConfig = { semicolons: true }; const formatter = createFormatter(prettierConfig); // 錯誤
在 TypeScript 2.4 以前,這段代碼是類型正確的。PrettierConfig
的全部屬性都是可選的,因此徹底能夠不指定它們。相反,我們的prettierConfig
對象有一個semicolons
屬性,它在prettierConfig
類型中不存在。
從 TypeScript 2.4 開始,當屬性沒有重疊時,給弱類型賦值是一個錯誤,帶有如下消息的類型檢查器錯誤
類型「{ semicolons: boolean; }」與類型「PrettierConfig」不具備相同的屬性
雖然我們的代碼並不是嚴格錯誤,但它可能包含一個靜默錯誤。createFormatter
函數可能會忽略它不知道的config
的任何屬性(例如semicolons
),並退回到每一個屬性的默認值。在這種狀況下,不管將其semicolons
設置爲true
仍是false
,我們的semicolons
屬性都不會起做用。
TypeScript 的弱類型檢測幫助我們解決了這個問題,並在函數調用中爲prettierConfig
參數提出了一個類型錯誤。這樣,我們很快就會意識到有些事情看起來不對勁。
無需依賴弱類型檢測,我們能夠向prettierConfig
對象顯式添加類型註釋:
const prettierConfig: PrettierConfig = { semicolons: true // Error }; const formatter = createFormatter(prettierConfig);
使用了這個類型註釋,我們會獲得如下類型錯誤:
不能將類型「{ semicolons: boolean; }」分配給類型「PrettierConfig」。 對象文字能夠只指定已知屬性,而且「semicolons」不在類型「PrettierConfig」中。
這樣,類型錯誤就出如今我們(錯誤地)定義semicolons
屬性的地方,而不是將prettierConfig參數傳遞給createFormatter
函數的行中。
另外一個好處是 TypeScript 語言能夠給我們自動完成建議,由於類型註釋告訴它咱建立的對象的類型。
若是出於某種緣由,我們就是不想從特定弱類型的弱類型檢測中得到錯誤,該怎麼辦?一種解決方法是使用unknown
類型添加索引簽名到PrettierConfig
類型:
interface PrettierConfig { [prop: string]: unknown; printWidth?: number; tabWidth?: number; semi?: boolean; } function createFormatter(config: PrettierConfig) { // ... } const prettierConfig = { semicolons: true }; const formatter = createFormatter(prettierConfig);
如今,這段代碼是類型正確的,由於我們在PrettierConfig
類型中明確容許使用unknown
名稱的屬性。
或者,我們可使用類型斷言來告訴類型檢查器將prettierConfig
對象視爲類型爲PrettierConfig
:
interface PrettierConfig { printWidth?: number; tabWidth?: number; semi?: boolean; } function createFormatter(config: PrettierConfig) { // ... } const prettierConfig = { semicolons: true }; const formatter = createFormatter( prettierConfig as PrettierConfig );
建議不要使用類型斷言來繞過弱類型檢。也許在一個用例中,這種方法是有意義的,可是一般,我們應該更喜歡其餘解決方案之一。
請注意,弱類型檢測僅在屬性中徹底沒有重疊時纔會產生類型錯誤。一旦指定了弱類型中定義的一個或多個屬性,編譯器將再也不引起類型錯誤
interface PrettierConfig { printWidth?: number; tabWidth?: number; semi?: boolean; } function createFormatter(config: PrettierConfig) { // ... } const prettierConfig = { printWidth: 100, semicolons: true }; const formatter = createFormatter(prettierConfig);
在上面的例子中,同時指定了printWidth
和semicolons
。由於printWidth
存在於PrettierConfig
中,如今我們的對象和PrettierConfig
類型之間有一個屬性重疊,弱類型檢測再也不爲函數調用引起類型錯誤。
這裏的結論是,弱類型檢測目的設計是爲了最小化誤報(正確的使用被視爲不正確)的數量,這是以犧牲更少的真報(不正確的使用被視爲不正確)爲代價的。
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
原文:
https://mariusschulz.com/blog...
https://mariusschulz.com/blog...
https://www.tslang.cn/docs/re...
乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。
https://github.com/qq44924588...
我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!
關注公衆號,後臺回覆福利,便可看到福利,你懂的。