前言

在 TypeScript 的類型系統中, 有四種特殊類型經常讓人混淆: undefinednullunknownnever. 它們各自有不同的語意和使用場景, 正確理解這些類型是寫出型別安全程式碼的基礎.

這篇文章將逐一介紹這四種類型的特點與使用方式, 並透過實際範例來說明它們的差異. 最後會整理一份最佳實踐與常見注意事項, 幫助你在日常開發中做出正確的選擇.

undefined

undefined 代表變數已宣告但尚未賦值, 是 JavaScript 中未初始化變數的默認值.

undefined 的特點

  • 表示變數存在但沒有值
  • 是變數的預設狀態
  • 常用於可選參數

undefined 的使用場景

以下範例展示 undefined 在可選屬性、未初始化變數和可選參數中的應用:

// 可選屬性
interface User {
  name: string;
  age?: number; // 可能是 undefined
}

// 未初始化的變數
let userInput: string | undefined;

// 可選參數
function greet(name?: string) {
  console.log(name ? `Hello ${name}` : "Hello");
}

在這個範例中:

  1. age? 表示該屬性是可選的, 未提供時值為 undefined
  2. userInput 明確宣告可能為 undefined
  3. name? 參數在未傳入時自動為 undefined

null

null 表示一個刻意被設置為「空值」的變數, 與 undefined 不同, 它需要明確賦值.

null 的特點

  • 表示刻意的空值
  • 需要明確賦值
  • 用於表示當前無效或不可用的狀態

null 的使用場景

以下範例展示 null 在狀態管理和 API 響應中的應用:

// 表示不存在的狀態
class UserService {
  private currentUser: User | null = null;

  logout() {
    this.currentUser = null; // 明確設置空值
  }
}

// API 響應可能為空
interface ApiResponse {
  data: Data | null;
  error: Error | null;
}

在這個範例中:

  1. currentUser 使用 null 表示「目前沒有登入的使用者」
  2. API 響應中的 dataerror 使用 null 表示「該欄位目前沒有值」

unknown

unknown 是 TypeScript 中最安全的「不確定」類型, 比 any 更嚴格.

unknown 的特點

  • any 更安全
  • 需要類型檢查才能使用
  • 不能直接訪問屬性或調用方法

unknown 的使用場景

以下範例展示如何搭配類型檢查和類型守衛來安全地處理 unknown 類型:

// API 響應處理
function processApiResponse(response: unknown) {
  // 需要類型檢查
  if (typeof response === "string") {
    console.log(response.toUpperCase());
  }

  // 使用類型守衛
  if (isUser(response)) {
    console.log(response.name);
  }
}

// 類型守衛
function isUser(value: unknown): value is User {
  return typeof value === "object" && value !== null && "name" in value;
}

在這個範例中:

  1. response 的類型是 unknown, 不能直接使用
  2. 透過 typeof 進行類型檢查後, TypeScript 會自動收窄類型
  3. 自訂類型守衛 isUser() 讓我們可以安全地存取 User 的屬性

never

never 表示永遠不會發生或永遠不會有返回值的類型.

never 的特點

  • 表示不可能的狀態
  • 用於永不返回的函數
  • 用於完整性檢查 (exhaustive check)
  • 在類型運算中作為過濾器

never 的使用場景

以下範例展示 never 在錯誤拋出、完整性檢查和類型運算中的應用:

// 永不返回的函數
function throwError(message: string): never {
  throw new Error(message);
}

// 完整性檢查
type Color = "red" | "green" | "blue";

function handleColor(color: Color) {
  switch (color) {
    case "red":
      return "處理紅色";
    case "green":
      return "處理綠色";
    case "blue":
      return "處理藍色";
    default:
      // 確保所有可能的值都被處理
      const exhaustiveCheck: never = color;
      return exhaustiveCheck;
  }
}

// 類型運算
type NonNullable<T> = T extends null | undefined ? never : T;

在這個範例中:

  1. throwError() 永遠不會正常返回, 所以回傳類型是 never
  2. default 分支利用 never 確保所有 Color 值都被處理過——如果未來新增了顏色卻忘了處理, TypeScript 會報錯
  3. NonNullable 利用 never 在條件類型中過濾掉 nullundefined

類型比較

類型用途示例場景
undefined未初始化的變數可選參數, 未賦值的變數
null明確的空值清除狀態, 表示不存在
unknown不確定的類型API 響應, 外部數據
never不可能的類型異常處理, 完整性檢查

最佳實踐

undefined vs null 的選擇

  • 優先使用 undefined 表示「可能不存在」
  • 使用 null 表示「明確的空值」
  • 在同一個專案中保持一致性

型別檢查方式

// 檢查 null
if (value !== null) {
}

// 檢查 undefined
if (value !== undefined) {
}
if (typeof value !== "undefined") {
}

// 檢查 unknown
if (typeof value === "string") {
}

使用 unknown 而不是 any

  • unknown 強制你進行類型檢查, 提供更好的類型安全性
  • 適合處理外部數據, 例如 API 響應或第三方套件回傳值

使用 never 進行完整性檢查

  • switch 語句中確保處理所有可能的值
  • 在類型運算中過濾不可能的情況

注意事項

避免使用 == 進行空值檢查

// ❌ 不推薦:會同時檢查 null 和 undefined
if (value == null) {
}

// ✅ 推薦:明確檢查
if (value === null) {
}
if (value === undefined) {
}

unknown 類型的值需要類型檢查

function process(value: unknown) {
  // ❌ 錯誤:不能直接使用
  value.toString();

  // ✅ 正確:先進行類型檢查
  if (typeof value === "string") {
    value.toString();
  }
}

never 類型的正確使用

// 用於確保處理所有可能的情況
function assertNever(x: never): never {
  throw new Error("Unexpected object: " + x);
}

總結

TypeScript 的四種特殊類型各有明確的語意和適用場景:

  • 🔹 undefined: 變數已宣告但尚未賦值, 適用於可選參數和屬性
  • 🔹 null: 刻意設置的空值, 適用於明確清除狀態的場景
  • 🔒 unknown: 比 any 更安全的不確定類型, 強制進行類型檢查後才能使用
  • 🚫 never: 不可能發生的類型, 適用於完整性檢查和類型過濾

記住兩個核心原則: 優先使用 unknown 取代 any 來處理外部數據, 以及善用 never 搭配 switch 進行完整性檢查, 能讓你的 TypeScript 程式碼更加型別安全.

參考資料