本文 AI 產出,尚未審核

TypeScript 課程 – 變數與常數宣告(Variables)

主題:顯式型別註記(Type Annotation)


簡介

在 JavaScript 中,變數的型別是由執行時的值決定的,這讓程式在大型專案或多人協作時容易出現不可預期的錯誤。TypeScript 透過 顯式型別註記(type annotation)讓開發者在編寫程式碼的同時,就能告訴編譯器「這個變數應該是什麼型別」,從而在編譯階段就捕捉到潛在的型別問題。

對於剛接觸 TypeScript 的新手而言,了解何時、如何正確地寫型別註記,是提升程式碼可讀性、可維護性以及減少除錯成本的關鍵一步。本章節將從基本概念出發,透過實作範例說明 顯式型別註記 的寫法與最佳使用時機,並提供常見陷阱與實務建議,幫助你在日常開發中善用 TypeScript 的型別系統。


核心概念

1. 為什麼要使用顯式型別註記?

優點 說明
提前發現錯誤 編譯時即能檢查型別不匹配,避免執行時例外。
提升 IDE 效能 具備完整型別資訊的程式碼,編輯器能提供更精準的自動完成與跳轉。
增進文件化 型別即是文件,閱讀程式碼時不必額外查閱說明文件。
加強團隊協作 明確的型別宣告讓不同開發者對變數的預期一致。

註:即使 TypeScript 擁有型別推論(type inference)功能,在公共 API、函式簽名或複雜結構上仍建議使用顯式註記,以避免推論錯誤或不易理解的情況。


2. 基本語法

let name: string = 'Alice';   // 變數 name 必須是 string
const age: number = 30;       // 常數 age 必須是 number
  • : 之後接型別名稱(string、number、boolean、any、unknown 等)。
  • let 用於可變的變數,const 用於不可變的常數。
  • 型別註記 只能寫在變數宣告時,若在賦值時才加入型別,會產生語法錯誤。

3. 常見型別的顯式註記

型別 範例 說明
string let title: string = 'Hello'; 文字資料
number let price: number = 99.9; 整數或浮點數
boolean let isActive: boolean = true; 真偽值
array let tags: string[] = ['ts', 'js']; 陣列,可寫為 Array<string>
tuple let point: [number, number] = [10, 20]; 固定長度且每個元素型別固定的陣列
enum enum Color { Red, Green, Blue }
let c: Color = Color.Green;
列舉型別
object let user: { name: string; age: number } = { name: 'Bob', age: 25 }; 具體的物件結構
any let data: any = fetchData(); 任意型別(盡量避免)
unknown let input: unknown = getUserInput(); 不確定但需先做型別檢查才能使用

4. 程式碼範例

範例 1:函式參數與回傳值的型別註記

// 這個函式接受兩個 number,回傳它們的總和
function add(a: number, b: number): number {
  return a + b;
}

// 使用時若傳入非 number,編譯器會直接報錯
// const result = add('1', 2); // ❌ TypeScript Error
const result = add(1, 2); // ✅ 正確

重點:函式的參數與回傳值都可以(且應該)加上型別註記,讓呼叫端明確知道預期的資料型別。


範例 2:物件屬性的型別註記

interface Product {
  id: number;
  name: string;
  price: number;
  tags?: string[]; // 可選屬性
}

// 建立符合介面的物件
const book: Product = {
  id: 101,
  name: '深入淺出 TypeScript',
  price: 350,
  // tags 可以不寫
};

說明:使用 interface 或 type 定義結構,然後在變數宣告時直接套用,這是顯式型別註記在大型專案中的常見寫法。


範例 3:陣列與 Tuple

// 字串陣列
let fruits: string[] = ['apple', 'banana', 'cherry'];

// 使用泛型寫法
let numbers: Array<number> = [1, 2, 3, 4];

// Tuple:固定長度且每個元素型別不同
let response: [number, string] = [200, 'OK'];
// response = [404, 'Not Found']; // ✅ 合法
// response = ['error', 500];    // ❌ 型別不匹配

範例 4:函式表達式與 Arrow Function

// 傳統函式寫法
const multiply: (x: number, y: number) => number = function (x, y) {
  return x * y;
};

// Arrow Function 寫法
const divide: (x: number, y: number) => number = (x, y) => x / y;

// 呼叫
const product = multiply(3, 4); // 12
const quotient = divide(10, 2); // 5

技巧:在變數上直接寫型別 (x: number, y: number) => number,可以一次描述參數與回傳型別,讓函式的意圖更清晰。


範例 5:使用 unknown 與型別保護

function parseJson(json: string): unknown {
  return JSON.parse(json);
}

const data = parseJson('{"name":"Tom","age":30}');

// 必須先做型別檢查才能使用
if (typeof data === 'object' && data !== null && 'name' in data) {
  // 這裡 TypeScript 仍認為 data 為 unknown,需要斷言
  const name = (data as { name: string }).name;
  console.log(name); // Tom
}

重點:unknown 比 any 更安全,必須先進行型別縮減(type guard)才能存取屬性或方法。


常見陷阱與最佳實踐

陷阱 說明 解決方式
過度使用 any 失去型別檢查的好處,等於回到純 JavaScript。 盡量使用具體型別或 unknown,必要時再用類型斷言。
忘記為函式回傳值加註記 編譯器會依賴推論,若邏輯變更可能產生不易發現的錯誤。 為所有公開函式明確標註回傳型別。
在 let 變數上寫 any 變數可被隨意改寫,導致型別不一致。 使用 const 搭配具體型別,或在需要變更時使用聯合型別(union)。
忽略可選屬性 (?) 的處理 直接存取未定義屬性會產生編譯錯誤。 加上型別保護或使用空值合併 (??)。
忘記 as 斷言的安全性 斷言過度會掩蓋真實錯誤。 只在確定資料結構時使用,盡量配合型別保護。

最佳實踐

  1. 先寫介面或型別別名:在實作前先定義 interface / type,再在變數上套用。
  2. 盡量使用 const:不變的值使用 const,讓編譯器自動推斷為字面量型別(literal type)。
  3. 結合型別推論:在簡單情況下讓 TypeScript 推論,但在公共 API、函式簽名或複雜結構上仍使用顯式註記。
  4. 使用 readonly:對於不允許修改的屬性,使用 readonly 以防止意外變更。
  5. 加入 JSDoc:即使有型別,適當的註解仍能說明業務意圖,提高可讀性。

實際應用場景

1. 前端表單資料驗證

在 React 或 Vue 中,表單的輸入值往往是字串,但後端需要特定型別(如 number、Date)。利用顯式型別註記可以提前捕捉轉型錯誤:

interface LoginForm {
  email: string;
  password: string;
  rememberMe?: boolean;
}

function submit(form: LoginForm): void {
  // 這裡 form 的結構已被明確限定
  api.post('/login', form);
}

2. Node.js 後端 API 回傳型別

在 Express 或 NestJS 中,回傳的 JSON 需要固定結構,使用型別註記可避免前端解析錯誤:

interface ApiResponse<T> {
  success: boolean;
  data: T;
  error?: string;
}

function getUser(id: number): Promise<ApiResponse<User>> {
  return db.findUser(id).then(user => ({
    success: true,
    data: user,
  }));
}

3. 大型團隊協作的程式庫開發

當開發公共套件或 UI 元件庫時,提供完整的型別宣告是必備。顯式型別註記讓使用者在 IDE 中即能看到每個屬性的型別,降低學習成本:

export type ButtonProps = {
  label: string;
  disabled?: boolean;
  onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
};

export const Button: React.FC<ButtonProps> = ({ label, disabled, onClick }) => (
  <button disabled={disabled} onClick={onClick}>{label}</button>
);

總結

  • 顯式型別註記 是 TypeScript 的核心特性之一,能在編譯階段即捕捉錯誤、提升 IDE 體驗,並讓程式碼自帶文件。
  • 在 變數、常數、函式參數與回傳值、物件結構 等關鍵位置加入型別註記,可大幅降低後期除錯成本。
  • 避免過度使用 any,善用 unknown、readonly、interface / type,並配合型別保護提升安全性。
  • 實務上,從 前端表單驗證、後端 API 回傳 到 公共套件開發,顯式型別註記皆能提供明確的合約,讓團隊協作更順暢。

掌握了顯式型別註記的寫法與最佳實踐,你將能在 TypeScript 專案中寫出更可靠、更易維護的程式碼。祝你在 TypeScript 的旅程中越走越遠,寫出乾淨且安全的程式!