TypeScript - 型別推論與聯合型別
型別推論 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
函式時,它會根據輸入的資料的型別顯示對應的商品資訊。這樣我們就可以同時處理不同型別的商品資料,而不需要為每種型別寫不同的函式。
聯合型別的常用情境
聯合型別在以下的情境下很有用:
處理不確定的資料型別:當我們在處理用戶輸入、 API 回應等資料時,這些型別的資料可能是不確定的。使用聯合型別可以容納多種可能的型別,避免錯誤。
提供多種選項:有時候你想讓一個變數可以存放多種選項,比如一個函式的參數可以是數字或字串。
處理不同型別的集合:在集合中,比如陣列或物件,可能包含不同型別的元素。使用聯合型別可以確保集合中的元素可以是其中之一。
存取聯合型別的屬性和方法
當 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
不是 string
與 number
的共同屬性,因此會報錯。
如果是要存取 string
和 number
的「共同屬性」是沒問題的:
function getLength(something: string | number): number {
return something.toString().length
}
上述例子中,先用 string
和 number
兩種型別共有的 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 會根據型別推論來推斷 mixedValue
為 string
型別。此時,存取它的 length
屬性不會報錯。
當我們再次賦值數字 7 給 mixedValue
變數,此時的 mixedValue
被推斷成了 number
型別。而 length
屬性並不存在於 number
型別中,因此會報錯。