iT邦幫忙

2024 iThome 鐵人賽

DAY 30
1
JavaScript

我推的TypeScript 操作大全系列 第 30

我推Day30 - API 開發再也不怕,TypeScript 進階型別駕馭術

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20241014/20124462ngG3sOy9p6.jpg

用工具型別定義每個回應情境,讓程式碼更可靠

各位開發小夥伴們,有沒有覺得每次處理 API 回應時都像是在猜謎一樣?

總是不確定回應裡到底會有什麼參數,每次都要額外檢查還特別麻煩。

別擔心!今天我要教大家用 TypeScript 的工具型別來統一管理這些回應型別,讓程式碼更直觀、開發更輕鬆,還能告別那些多餘的錯誤!我們一起來看看怎麼做到吧!


API 回應型別的挑戰 🤔

開發者每天都在和 API 打交道,當操作成功或出現錯誤時,我們通常會依照標準化的 API 回應來進行開發,比如透過 statuserror 來決定後續的行為。假設註冊成功,我們可以隱藏表單並顯示成功訊息;如果資料格式錯誤,我們則要顯示驗證錯誤的資訊。但如何方便、快速、靈活地描述這些回應型別呢?

問題描述

有時候,我們習慣於用一個包含多個可選參數的單一型別來描述所有的回應型別,這看起來似乎很簡單,但隨之而來的問題是缺乏透明度與靈活性。

比如這樣定義:

type ApiData = {
  status?: string;
  error?: string;
  errors?: string[];
};

這樣的定義雖然很簡單,卻會讓我們在寫程式碼時,對每個回應的內容缺乏明確的了解。而且不同的請求其實可能只有少部分參數需要出現,例如 GET 請求通常不會有 errors 欄位。每次都用同一型別描述不同的回應,不僅增加了額外的型別檢查,還讓整個程式碼變得不夠直觀。


使用工具型別解決問題 🛠️

為了讓 API 回應型別的描述更加精確,我們可以透過自訂的工具型別來解決這些問題。針對每種情境(例如操作成功、伺服器錯誤、驗證錯誤、強制重新導向),可以分別定義不同的工具型別。

這樣的好處在於,每個情境下,我們可以清楚知道該回應有哪些欄位。比如:

type SuccessResponse<T> = {
  status: 'ok';
  data: T;
};

type ValidationErrorResponse = {
  status: 'form_errors';
  errors: string[];
};

有了這些工具型別,我們可以針對不同的回應情境來組合使用,而不再需要一個包含多個可選欄位的「萬用型別」。
這樣的好處是,我們可以避免不必要的型別檢查,並能夠直接明白每種情境下有哪些欄位需要處理。

實際應用

當我們用這些工具型別來描述 API 回應時,程式碼變得更加清晰。
比如說,我們有一個更新使用者資料的函數,它的回應可能是成功或驗證錯誤,我們可以這樣定義:

type UpdateUserResponse = SuccessResponse<{ user: { id: number; name: string; } }> | ValidationErrorResponse;

這樣一來,我們就能夠確保在處理回應時,不會出現不存在的欄位,也不會因為忽略了一些回應而導致錯誤。


內建工具型別的擴展應用 🔄

除了自訂的工具型別,TypeScript 也提供了一些內建工具型別,例如 PartialPickOmit,可以和自訂的工具型別結合,進一步提升靈活性和可重用性。

例子:使用 Omit 排除欄位

假設我們有一個包含敏感資料的使用者型別,但在某些 API 回應中我們不想暴露這些敏感資料,我們可以使用 Omit 工具型別來排除不需要的欄位:

type User = {
  id: number;
  name: string;
  password: string;
};

type PublicUser = Omit<User, 'password'>;

這樣,我們就能得到一個沒有 password 欄位的 PublicUser 型別,非常適合用在公開 API 回應中,確保不會不小心暴露敏感資訊。

這樣的擴展應用可以幫助我們更精確地控制回應型別,同時提升開發靈活性。


型別守護的實際運用 🛡️

在處理 API 回應時,型別守護(Type Guards)也是一個非常有用的工具,能幫助我們進一步保證型別安全。透過型別守護,我們可以精確地檢查某個回應屬性,並執行對應的操作。

例子:使用型別守護進行安全檢查

假設我們有一個 API 回應的工具型別,包含成功和錯誤的回應狀態,我們可以使用型別守護來檢查回應是否成功:

type ApiResponse<T> = SuccessResponse<T> | ValidationErrorResponse;

function isSuccessResponse<T>(response: ApiResponse<T>): response is SuccessResponse<T> {
  return response.status === 'ok';
}

function handleApiResponse<T>(response: ApiResponse<T>) {
  if (isSuccessResponse(response)) {
    console.log('成功:', response.data);
  } else {
    console.error('錯誤:', response.errors);
  }
}

在這個例子中,我們使用 isSuccessResponse 型別守護來檢查回應是否成功,這樣 TypeScript 就能知道在 if 區塊內,我們可以安全地訪問 data,而不必擔心型別錯誤。

這樣的型別守護在實際應用中非常實用,特別是在處理多種回應狀態時,可以讓程式碼更加簡潔且安全。


實際差異:開發體驗的大提升 🚀

當我們使用單一型別來描述所有的回應情境時,會出現一些問題。例如:

function handleResponse(data: ApiData) {
  if (data.status === 'ok') {
    console.log(data.user.id); // 這裡沒有保證 user 存在
  }
}

在這段程式碼中,TypeScript 無法確保 user 存在,因為 ApiData 型別中 user 是可選的,這會讓我們在操作時遇到不必要的型別錯誤。

但是,當我們使用前面提到的工具型別時,就能避免這樣的情況發生:

function handleResponse(data: UpdateUserResponse) {
  if (data.status === 'ok') {
    console.log(data.data.user.id); // 這裡 TypeScript 知道 user 一定存在
  }
}

在這裡,TypeScript 確保了當 status'ok' 時,data 一定存在並且有 user,這讓我們的程式碼更加安全和易於維護。


小結:掌握工具型別的力量 💡

📌 標準化 API 回應
透過工具型別,我們可以針對每一種 API 回應情境進行精確描述,讓程式碼更加清晰易讀。

📌 避免不必要的型別檢查
透過精確的型別定義,我們可以避免不必要的型別檢查,讓程式更加精簡且可維護。

📌 與內建工具型別結合使用
結合使用 OmitPartial 等內建工具型別,可以讓 API 回應型別的設計更加靈活且精確。

📌 提升開發體驗
使用工具型別來描述 API 回應,可以顯著提升開發體驗,不需要再翻看程式碼來確認回應的細節。

📌 型別守護的應用
使用型別守護來精確檢查 API 回應狀態,讓程式碼更安全且簡潔。


TypeScript 的型別系統是如此強大而靈活,透過工具型別的巧妙應用,我們能大幅提升程式的可靠性與維護性。希望今天的分享能讓你在處理 API 回應時更得心應手,讓你的 TypeScript 開發之旅更加順暢!🎉


上一篇
我推Day29 - 從可變元組到 Opaque Types,揭開 TypeScript 型別系統的大心法
系列文
我推的TypeScript 操作大全30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言