Back To Blog
June 10, 2025

別再 if else:用 Discriminated Union 精準控制型別邏輯

本篇文章介紹 TypeScript 中的 Discriminated Union,並透過實例說明如何用它優雅地取代 if-else 判斷,讓型別幫你寫邏輯、降低錯誤風險、提升維護性。

TypeScript

toc

情境

小明入職後負責了一個新專案,這天他要來寫後端回傳的 API 規格,有成功或失敗兩種,成功的話會回傳

{
  "status": "success",
  "data": "test"
}

失敗的話則會:

{
  "status": "error",
  "error": "Something went wrong"
}

小明一拍腦袋,想說那就這樣定義 APIResponse 好了

type APIResponse = {
  status: "success" | "error";
  data?: any;
  error?: any;
};

試問,上述寫法會造成什麼影響?


沒錯,經過一兩週之後,小明要開始串接 API,卻發現每次處理 API Response 都需要寫 if-else

function handleResponse(res: APIResponse) {
  if (res.status === "success") {
    // 處理成功邏輯
  } else if (res.status === "error") {
    // 處理錯誤邏輯
  } else {
    throw new Error("Unknown response");
  }
}

你也有遇到類似的困擾嗎?當型別有兩種狀況,為求簡單將不同欄位寫成 optional 狀況,反而導致後續處理變複雜,這時候可以考慮使用 Discriminated Union

什麼是 Discriminated Union?

根據 TypeScript 官網 說法:

A common technique for working with unions is to have a single field which uses literal types which you can use to let TypeScript narrow down the possible current type.

簡單講就是一種特定的 Union Type,其成員包含一個可以識別的共同欄位。

可以來改寫上面的 APIResponse,他們都有一個共同欄位叫做 status ,但成功跟失敗回傳的 status 值不同。

type Success = { status: "success"; data: any };
type Error = { status: "error"; error: any };
type APIResponse = Success | Error;

在處理邏輯時可以改寫成:

function handleResponse(res: APIResponse) {
  switch (res.status) {
    case "success":
      // 處理成功
      break;
    case "error":
      // 處理失敗
      break;
    default:
      const _exhaustiveCheck: never = res;
      return _exhaustiveCheck;
  }
}

結語

使用 Discriminated Union 的好處是可以清楚的定義資料結構,而不會不同狀態的型別混在一起,都變成 optional,使用 switch 來處理邏輯讓型別判斷變得更清楚。

Related Posts