[學習筆記]TypeScript查缺補漏(二):型別與控制流分析

2023-11-01 21:00:19

@

型別約束

TypeScript中的型別是一種用於描述變數、函數引數和函數返回值的特徵的方式。

程式碼中定義型別的部分不會在原生JavaScript環境中編譯

基本型別

包括number、string、boolean、null、undefined和symbol。

聯合型別

聯合型別(Union Types)是指用「|」符號將多個型別組合成一個的型別。這意味著一個變數可以儲存多種型別的值

let value: number | string = 42;

控制流分析

TypeScript 本身是一種靜態型別語言,它在編譯時進行型別檢查,因此控制流分析在編譯階段就已經開始了

比如:

function getUserInput():string|number{
  if(new Date() != new Date("2023-11-01")){
    return "yes";
  }
  else {
    return 0;
  }
}

此時Typescript的控制流分析將data型別視為 string | number

instanceof和typeof

typeof操作符用於獲取一個值的型別。它返回一個字串,表示值的型別

instanceof操作符用於判斷一個物件是否屬於某個類的範例

使用typeof input === "string" 和 input instanceof String 這兩個判斷有什麼區別:

typeof只能用於判斷基本型別,instanceof可以用於判斷自定義的型別,例如類,介面,陣列等
instanceof 可以用於判斷一個物件是否是某個類的範例,包括繼承自該類的子類。

型別守衛和窄化

型別守衛用於獲取變數型別資訊,通常使用在條件塊語句中。型別守衛是返回布林值的常規函數,接受一個型別並告訴 TypeScript 是否可以縮小到更具體的型別。

縮小到更具體的型別的過程稱之為「窄化(Narrowing)」,它能根據程式碼中的邏輯減少聯合中的型別數量。大多數窄化來自if語句中的表示式,其中不同的型別操作符在新的範圍內窄化。

typeof判斷

var input = getUserInput()
input  // string |number
if (typeof input === "string") {
  input  // string
}

instanceof判斷

var input = getUserInput()
input  // number | number[]
if (input instanceof Array)) {
  input  // number[]
}

in判斷

var input = getUserInput()
input  // string | {error: ...}
if ("error" in input) {
  input  // {error: ...}
}

內建函數,或自定義函數


var input = getUserInput()
input  // number | number[]
if (Array.isArray(input)) {
  input  // number[]
}


// 驗證是否是number型別
function isNumber(x: any): x is number {
  return typeof x === "number";
}

// 驗證是否是string型別
function isString(x: any): x is string {
  return typeof x === "string";
}


var input=getUserInput(); //string | number
if (isNumber(input)){
  input // number
}

賦值

賦值過後的變數,也將發生「窄化」

let data: string | number = ..
data // string | number
data ="Hello"
data // string

布林運算

執行布林運算時在一行程式碼裡也會發生窄化

var input = getUserInput()
input  // number | string
const inputLength = (typeof input ==="string" && input.length) || input //string

保留共同屬性

當聯合型別的所有成員都有相同的屬性名稱時,這些屬性會被保留

type Responses =
|{ status: 200, data: any }
|{status :301, to: string }
|{status: 400,error: Error }

const response = getResponse()
response // Responses
switch(response.status) {
  case 200: return response.data
  case 301: return redirect(response.to)
  case 400: return response.error
}

字面量型別(literal type)

字面量型別指的是使用字面量語法來定義的型別。字面量型別是一種特殊的型別,它允許你使用字面量值來定義型別的約束。

字面量型別包括以下幾種:

  1. 數位字面量型別:使用數位字面量來定義數位型別的約束。例如,0、1、2 等。
  2. 字串字面量型別:使用字串字面量來定義字串型別的約束。例如,"hello"、"world" 等。
  3. 布林字面量型別:使用布林字面量 true 和 false 來定義布林型別的約束。
  4. 元組字面量型別:使用元組字面量 [item1, item2, ...] 來定義元組型別的約束。例如,[1, "hello"] 表示一個包含一個數位和一個字串的元組。
  5. null 和 undefined 字面量型別:使用 null 和 undefined 字面量來表示 null 和 undefined 型別的約束。
  6. 空型別字面量:使用 {} 來定義一個空型別的約束,表示該型別沒有任何屬性或方法。
  7. 任意值字面量型別:使用 any 關鍵字來定義任意值的約束,表示該型別可以接受任何型別的值。

編譯器在編譯期間進行型別檢查,提高程式碼的可讀性和可維護性。

通常字面量型別不會單獨出現,而是配合聯合型別,用於約束型別的值

declare function handleRequest(url: string, method: "GET" | "POST"): void;

handleRequest(req.url, "GET");  //編譯通過

handleRequest(req.url, "MYGET");  //編譯不通過,值不合法

as const 作用

使用「as const」將型別鎖定為其字面量版本

在 TypeScript 中,as const 用來明確告訴 TypeScript 編譯器這個變數是唯讀的(const),並且它的型別是字面量型別

還是剛剛的例子

declare function handleRequest(url: string, method: "GET" | "POST"): void;
const req = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method);

編譯器會報錯

原因是req型別被識別為

const req = { url: "https://example.com", method: "GET" } as const;

當加上「as const」識別為字面量版本的型別

-本章完-