跳到主要内容

type(型別)-駐守在 TypeScript 最前線的防禦機制

轉載自: 讓 TypeScript 成為你全端開發的 ACE | Maxwell Alexius

範例代碼 | Alexius-Huang Iron-Man-Competition

型別推論 Inference

基本上 ts 會自動推論那你寫的變數的 型別監控

let myName = 'Maxwell';
let age = 20;
let hasPet = false;
let nothing = undefined; // 推論為 any(失敗,無法監控)
let nothingLiterally = null; // 推論為 any(失敗,無法監控)

any 是造成型別混亂的根源

Nullable Types 會被推論為 any 型別,而 any 在 TypeScript 裡會無法監督變數狀態,造成程式碼的混亂。 因此我們應當儘量避免這件事情的發生:變數的型別被視為 any

遲滯性指派 Delayed Initialization

每當我們對任何變數不立即指派值,該變數會無條件被視為 any 型別。

// 我們先定義好我們的變數,但是不要去帶入任何值
let messageToSend;

// 之後帶入我們想要寫的值
messageToSend = 'Cat is afraid of cucumbers...';
messageToSend = 1011011011100100011111010110;

其中 TS 一開始就已經認定好 messageToSend 就是 any 型別。因此不管你後續代入什麼值進去,TS 都無所謂,完全放棄閒置狀態。

型別註記(Type Annotation)

正確寫法

對這些被指派 Nullable Types 的變數或者 不立即被指派值的變數做 型別註記(Type Annotation)

let absolutelyEitherNullOrString: string | null = null; // 使用 Union Types(聯合型別) 

absolutelyEitherNullOrString = 'Assigned with string...';
absolutelyEitherNullOrString = null;
absolutelyEitherNullOrString = 'Assigned with string, again...';
//此時 absolutelyEitherNullOrString 可為 null 或 string

物件型別

會自動推論變數中的屬性。

let info = {
name: 'Maxwell',
age: 20,
hasPet: false,
};
//會提醒:“這個變它還存在 name、age 以及 hasPet 的屬性,對應型別為字串、數字以及布林代數。“

let someone = {
knows: undefined,
// Object 沒有 Nullable Types 問題
identity: null
// Object 沒有 Nullable Types 問題
};

廣義物件完整性定律

廣義物件完整性定律(非官方,但好記)

廣義物件在被 TypeScript 推論的狀態下,屬性不能被任意新增或更改成其他型別。

能夠做的事情只有:

  1. 全面覆寫,廣義物件的屬性對照型別格式也要完全對位
  2. 更改廣義物件本身就擁有屬性對應的值,其中:要帶入的值的型態必須對應到該屬性的型態

我們稱這樣的行為為「保持廣義物件的完整性」。

函式型別 Function Types

重點 1. Implicit Any

let addition = function (num1, num2) {
return num1 + num2;
};
//會提醒:“Parameter 'num1' implicitly has an 'any' type.“

大部分的情況下,只要定義任何函式,TypeScript 通常會無條件推論函式內的參數(Parameters)為 any 型別,這種現象我們稱之為 Implicit Any。

重點 2. 函式的推論與註記

輸入註記

let addition = function (param1: number, param2: number) {
return param1 + param2;
};

let shouldBeString: string = addition(123, 456); // => 出錯!
//會提醒:Type 'number' is not assignable to type 'string'.ts(2322)

輸出註記

let addition = function (param1: number, param2: number): number {
return param1 + param2;
};

分別為輸入參數與輸出部分,大部分情況下,只要我們提供函式參數的註記,輸出就可以間接被 TypeScript 推論出來

重點 3. 函式回傳 any 型別(自己需手動註記)

有些情況設計出來的函式,其回傳的型別只能是 any~(一般來說會盡量避免)

這個就是筆者形容極少數狀況下會使用 any 的其中一種 Case:請看看 JSON.parse 這個函式(確切來說是方法,畢竟方法跟函式還是有差別)

// TS 不會鳥你的狀況(any)
let parsedJSON = JSON.parse(aJSONString);

// 接受 TS 型別系統的擁抱
let parsedJSON1 = JSON.parse(aJSONString) as { hello: string, luckyNumber: number };
let parsedJSON2 = <{ hello: string, luckyNumber: number }>JSON.parse(aJSONString);
let parsedJSON3: { hello: string, luckyNumber: number } = JSON.parse(aJSONString);

遇到函式是回傳 any 型別的值,我們必須主動對該值作型別註記(Type Annotation),找回開發 TypeScript 的優勢 —— 也就是 TS 提供的型別系統(Type System)

陣列型別 Array Types

提示

不清楚廣義和狹義物件的讀者,這是為了好講解這一系列而定義出來的詞彙,分別代表:

  • 狹義物件:JSON 格式物件
  • 廣義物件:JSON 格式物件、陣列、函式、類別與藉由類別創建出來的物件...等等。(也可以想成所有型別的集合對原始型別進行差集的動作)

重點 1. 陣列的型別推論

若集合 S 為陣列裡所有元素各種型別的集合,大部分的情形下,該陣列被 TypeScript 型別推論的結果是:

(集合 S 裡所有型別 `union` 的結果)[]

介面()