TypeScript - 型別推論與聯合型別

·

2 min read

型別推論 Type Inference

變數在宣告的時候如果沒有明確指定任何型別,TypeScript 就會依照型別推論(Type Inference)的規則來推測出變數的型別。

變數的型別推論

let something = 'Hello' // 等同於 let something: string = 'Hello'
something = 7

// index.ts: error TS2322: Type 'number' is not assignable to type 'string'

因為變數 something 的初始值為字串,TypeScript 編譯器便會推斷其型別為 string。如果初始值是數字、布林值⋯⋯等其他型別,編譯器也會推斷出相應的型別。

因此 let something = 'Hello' 事實上等同於 let something: string = 'Hello'

函式參數和返還值型別推論 留言

function add(a, b) {
    return a + b
}

// 等同於
function add(a: number, b: number): number {
    return a + b
}

未宣告型別與初始值的變數型別推論

上一篇所述,如果變數在宣告的時候既沒有指定任何型別也沒有給予初始值,TypeScript 便無法推斷出變數的型別,因而會將變數標示為 any 型別。

let something // 等同於 let something: any

而當一個變數的型別為 any 時:

  • 可以將任何型別的值賦予給它(不管之後有沒有賦值都仍會是 any 型別)

  • 並且對它的任何操作所返回的值的型別也都會是 any

這樣等於是門戶大開、完全跳過了 TypeScript 的型別檢查。

聯合型別 Union Types

TypeScript 中的一種進階型別,用來表示允許變數可以是多個不同型別。

聯合型別使用垂直線 | 來分隔不同的型別。

let value: string | number // value 可以是 string 或 number
value = 'seven' // 正確, value 可以是 string
value = 7 // 正確,value 可以是 number
value = true // 錯誤,value 不能是 boolean

這裡的 someValue 只能是 string 或是 number,不能是其他型別。

為什麼會需要聯合型別

聯合型別在 TypeScript 中的作用是允許一個變數具有多種不同的資料型別,這可以讓我們更靈活地處理變數可能具有多種不同型別的狀況,像是多樣化的輸入或資料。

假設我們正在開發一個商品訂單系統,其中一個功能是要顯示商品的資訊。而每個商品都有其獨特的特性。有些商品可能是數字,代表數量;有些則是文字,代表商品名稱。

這時候,你可以使用聯合型別來處理這些可能的資料型別。

function displayProductInfo(product: number | string) {
    if (typeof product === "number") {
        // 如果輸入的是數字(例如 3),則代表著商品數量
        console.log(`Quantity: ${product}`);
    } else {
        // 如果輸入的是字串(例如 "Apple"),則代表著商品名稱
        console.log(`Product Name: ${product}`);
    }
}

displayProductInfo(3);        // 輸出:Quantity: 3
displayProductInfo("Apple");  // 輸出:Product Name: Apple

這個例子中,product 是一個聯合型別,可以是 number 或是 string。當我們呼叫 displayProductInfo 函式時,它會根據輸入的資料的型別顯示對應的商品資訊。這樣我們就可以同時處理不同型別的商品資料,而不需要為每種型別寫不同的函式。

聯合型別的常用情境

聯合型別在以下的情境下很有用:

  1. 處理不確定的資料型別:當我們在處理用戶輸入、 API 回應等資料時,這些型別的資料可能是不確定的。使用聯合型別可以容納多種可能的型別,避免錯誤。

  2. 提供多種選項:有時候你想讓一個變數可以存放多種選項,比如一個函式的參數可以是數字或字串。

  3. 處理不同型別的集合:在集合中,比如陣列或物件,可能包含不同型別的元素。使用聯合型別可以確保集合中的元素可以是其中之一。

存取聯合型別的屬性和方法

當 TypeScript 不確定一個聯合型別的變數到底是哪個型別的時候,只能存取此聯合型別的所有型別裡共有的屬性或方法:

function getLength(something: string | number): number {
    return something.length
}
// index.ts:2:22 - error TS2339: Property 'length' does not exist on type 'string | number'.
// Property 'length' does not exist on type 'number'.

上述例子中,因為 length 不是 stringnumber 的共同屬性,因此會報錯。

如果是要存取 stringnumber 的「共同屬性」是沒問題的:

function getLength(something: string | number): number {
    return something.toString().length
}

上述例子中,先用 stringnumber 兩種型別共有的 toString() 方法將輸入轉為字串再存取其 length 屬性。

聯合型別賦值與型別推論

聯合型別的變數在被賦值的時候,會根據型別推論的規則推斷出該變數的型別。

let mixedValue = string | number

mixedValue = 'hello' // TypeScript 推斷為 string 型別
console.log(mixedValue.length) // 5

mixedValue = 7 // TypeScript 推斷為 number 型別
console.log(mixedValue.length) // 會報錯
// error TS2339: Property 'length' does not exist on type 'number'.

當我們將字串 'hello' 賦值給 mixedValue 變數時, TypeScript 會根據型別推論來推斷 mixedValuestring 型別。此時,存取它的 length 屬性不會報錯。

當我們再次賦值數字 7 給 mixedValue 變數,此時的 mixedValue 被推斷成了 number 型別。而 length 屬性並不存在於 number 型別中,因此會報錯。

Ref