TypeScript 型別註解 (Type Annotations)

型別註解是我們「明確告訴」TypeScript 這個變數是什麼型別的方式。雖然 TypeScript 很聰明(有強大的型別推論),但有這兩種情況我們通常會明確寫出型別:

  1. 當 TS 猜不到時(例如函式參數)。
  2. 當我們想強制檢查時(例如確保函式回傳值符合預期)。

基本語法

語法很簡單,就是在變數或參數後面加上 : 型別名稱

變數註解 (Variables)

// 告訴 TS:這個 name 變數一定要是 string
let name: string = '小明';

// 錯誤!不能把 number 塞給 string
// name = 123;

小知識:其實上面這個例子不需要寫 : string,因為 TS 看到 "小明" 就知道它是字串了。這叫做「型別推論」,我們下一章會細講。

函式參數 (Detailed Functions)

這是最需要型別註解的地方!因為 TS 通常無法猜到你的函式會被怎麼呼叫。

// 定義:input 必須是 string
function logMessage(message: string) {
  console.log(message);
}

// 使用正確
logMessage('Hello');

// 報錯:TS 阻止你傳入錯誤的型別
// logMessage(123);

函式回傳值 (Return Type)

我們也可以規定函式「必須」回傳什麼型別。

// 這個函式接收兩個 number,並且保證回傳 number
function add(a: number, b: number): number {
  return a + b;
}

這在多人協作時特別好用,因為如果有人不小心改壞了函式邏輯(例如回傳了字串),TS 馬上會報錯。


複雜型別註解

陣列 (Arrays)

// 定義一個「只裝數字」的陣列
let numbers: number[] = [1, 2, 3];

// 錯誤!不能放字串進去
// numbers.push("4");

物件 (Objects)

我們可以直接在變數後面描述物件的「形狀」:

let user: {
  name: string;
  age: number;
} = {
  name: '小明',
  age: 25,
};

但通常我們會用 interfacetype 把這個形狀抽出來,讓程式碼更乾淨:

interface User {
  name: string;
  age: number;
}

let user: User = {
  name: '小明',
  age: 25,
};

常見問題

Q: 什麼時候該寫註解?什麼時候靠推論?

原則:能推論就讓它推論,不能推論(或推論結果太寬鬆)才寫註解。

  • ✅ 不用寫:簡單的變數賦值。

    let age = 18; // TS 自動知道這是 number
    
  • ✅ 一定要寫:函式參數。

    function printId(id: number) { ... }
    
  • ✅ 建議寫:函式回傳值(為了文件化和防止意外修改)。

    function getUser(): User { ... }
    

Q: 如果我真的不知道是什麼型別怎麼辦?

如果你正在從 JavaScript 遷移過來,或者處理一些很動態的資料,你可以暫時使用 any,但請記住這是下下策。

// 盡量避免
let data: any = JSON.parse(response);

// 比較好的做法是定義一個大致的介面,或者用 unknown
let safeData: unknown = JSON.parse(response);