本文 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
  • : 之後接型別名稱(stringnumberbooleananyunknown 等)。
  • 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 可以不寫
};

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


範例 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
}

重點unknownany 更安全,必須先進行型別縮減(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 中,表單的輸入值往往是字串,但後端需要特定型別(如 numberDate)。利用顯式型別註記可以提前捕捉轉型錯誤:

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,善用 unknownreadonlyinterface / type,並配合型別保護提升安全性。
  • 實務上,從 前端表單驗證後端 API 回傳公共套件開發,顯式型別註記皆能提供明確的合約,讓團隊協作更順暢。

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