【TS 演化史 -- 13】字符串枚舉 和 弱類型(Weak Type)探測

做者: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) {
    // ...
});

弱類型(Weak Type)探測

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);

在上面的例子中,同時指定了printWidthsemicolons。由於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...

我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!

關注公衆號,後臺回覆福利,便可看到福利,你懂的。

clipboard.png

相關文章
相關標籤/搜索