學習資源
(註:今天在翻閱這次的學習資源時,才發現昨天在讀任意值型別的過程中,似乎已經將型別推論講完了耶,不過沒關係可以當作複習。)
什麼是型別推論(Type Inference)?
在沒有明確指定型別的情況下,TypeScript 會根據程式碼中的上下文和賦值情況來判斷型別。以下舉幾個例子:
這是在寫例子的時冒出一個想法:想像我們戴上 TypeScript 眼鏡,就能在程式碼內看見 TypeScript 透過型別推論判斷出的型別。
TypeScript 眼鏡,帶你看見全世界。
變數型別推論
let message = "Hello, World!"; // TypeScript 推斷型別為 string
let age = 30; // TypeScript 推斷型別為 number
let isEnabled = true; // TypeScript 推斷型別為 boolean
戴上 TypeScript 眼鏡後會看到:
let message: string = "Hello, World!";
let age: number = 30;
let isEnabled: boolean = true;
函式參數和返回值型別推論
function sum(a, b) {
return a + b;
}
戴上 TypeScript 眼鏡後會看到:
function sum(a: number, b: number): number {
return a + b;
}
未宣告型別的變數型別推論
let someVariable;
戴上 TypeScript 眼鏡後會看到:
let someVariable: any;
什麼是聯合型別(Union Types)?
聯合型別是 TypeScript 中的一種進階型別,用來表示允許將多個不同的型別組合成一個型別。聯合型別用 |
符號來表示多個型別的組合。
(註:可以想像戴 TypeScript 眼鏡來看 JavaScript 的 OR 運算符號 ||
時會少一個 |
來記)
function displayValue(value: string | number) {
console.log(value);
}
displayValue("Hello"); // 🟢 正確:value 可以是 string
displayValue(42); // 🟢 正確:value 也可以是 number
displayValue(true); // 🔴 錯誤:value 不能是 boolean,因為它不是 string 或 number 的一部分
為什麼需要聯合型別?
(註:這是 ChatGPT 建議補充說明的部份,它用實際開發可能會遇到的需求當做範例,超有代入感,這個方式很讚學起來)
聯合型別讓 TypeScript 可以更靈活地處理變數具有多種不同型別的情況,以下是一個實際開發可能會遇到的應用場景:
假設我們正在開發一個線上商店的應用程式,其中一個功能是計算折扣後的價格。折扣可以是固定金額(例如,10元)或百分比(例如,10%)。為了處理這個情況,我們可以使用聯合型別來表示折扣:
function calculateDiscountedPrice(
price: number,
discount: number | string
): number {
if (typeof discount === "string") {
// 如果折扣是字串(例如 "10%"),則將其轉換為小數並應用
const percentage = parseFloat(discount) / 100;
return price * (1 - percentage);
} else {
// 如果折扣是數字(例如 10),則直接從價格中減去折扣
return price - discount;
}
}
console.log(calculateDiscountedPrice(100, "10%")); // 90
console.log(calculateDiscountedPrice(100, 10)); // 90
在這個例子中,我們使用聯合型別 Discount
來表示折扣,它可以是數字(固定金額)或字串(百分比)。calculateDiscountedPrice
函數根據折扣的型別計算折扣後的價格,從而滿足實際開發中的需求。
存取聯合型別共有的屬性和方法注意事項
在處理聯合型別的變數時,TypeScript 只允許存取所有型別共有的屬性和方法。
直接用一個會報錯的例子來說明:
function getLength(input: string | number): number {
return input.length; // 🔴 錯誤:因為 length 不是 string 和 number 的共有屬性
}
要解決這個錯誤,有兩個方式:
在函式內部對輸入進行型別檢查,以確保只在
input
為字串時存取length
屬性function getLength(input: string | number): number { if (typeof input === "string") { return input.length; // 正確:input 為 string,可以訪問 length 屬性 } else { return 0; // 如果 input 是數字,我們返回 0 或其他適當的值 } }
或是改用
string
和number
兩種型別都有的方法toString()
function getLength(input: string | number): number { return input.toString().length; }
聯合型別變數賦值時與型別推論之間擦出的火花
一樣用例子來說明,我們先定義一個聯合型別的變數:
let mixedValue: number | string;
這個聯合型別變數 mixedValue
表示它可以是 number
或 string
型別。接著,我們為這個變數賦值:
mixedValue = "hello"; // TypeScript 推論 mixedValue 為 string 型別
當我們把字串 "hello" 賦值給 mixedValue
變數時,TypeScript 會根據賦值來推論 mixedValue
的型別。因為 "hello" 是一個字串,所以 TypeScript 推論 mixedValue
是 string
型別。
如果我們再次為 mixedValue
賦值:
mixedValue = 42; // TypeScript 推論 mixedValue 為 number 型別
這次我們把數字 42 賦值給 mixedValue
變數。因為 42 是一個數字,所以 TypeScript 會推論 mixedValue
是 number
型別。
小心擦出危險的火花?
其實就是在上一段提到的注意事項:TypeScript 只允許存取所有型別共有的屬性和方法。
let mixedValue: number | string;
mixedValue = "hello";
console.log(mixedValue.length); // 🟢 不會報錯,因為 mixedValue 被推斷為 string 型別
mixedValue = 42;
console.log(mixedValue.length); // 🔴 會報錯,因為 length 屬性不存在於 number 型別
後記:為了確保自己能以初次翻閱這本書的狀態來紀錄筆記,我在計畫開始時並沒有事先瀏覽所有內容,只有掃過一遍目錄而已,所以每次在準備學習和寫筆記時,我才會知道內容的多寡和複雜程度。這過程有點像是開盲盒,不確定究竟打開會是驚喜(簡單)還是驚嚇(複雜)。
儘管內心難免會有些負擔,但我想這樣做才能捕捉並保留在第一次閱讀時所出現的疑問、靈感和想法,就算之後全部忘光再回來複習,我希望能透過筆記再次理解這些知識。