iT邦幫忙

2024 iThome 鐵人賽

DAY 24
0
JavaScript

用 TypeScript 重新定義前端開發:30 天的實踐與思考系列 第 24

Day24:使用 TypeScript 為異步操作與 Promise 添加型別加持

  • 分享至 

  • xImage
  •  

在 JavaScript 中,異步操作(例如網絡請求、讀取檔案等)經常使用 Promise 來處理,而 TypeScript 允許我們為這些 Promise 和異步函式添加型別支持,這樣可以讓程式碼更加可靠和易於維護。

今天的文章會用簡單的範例說明,如何透過 TypeScript 為異步操作和 Promise 提供型別,讓異步程式碼變得更強健。

一、什麼是 Promise?

Promise 是一個對象,用來表示一個尚未完成但將來會被解決的異步操作。它可以有三種狀態:

  • Pending:操作尚未完成。
  • Fulfilled:操作成功完成並返回結果。
  • Rejected:操作失敗並返回錯誤。

例如,我們可以使用 Promise 來處理 API 請求:

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

但在這樣的異步操作中,沒有明確的型別來保證我們的 data 是什麼格式。這時候,TypeScript 就能幫上大忙了!

二、使用 TypeScript 為 Promise 添加型別

在 TypeScript 中,我們可以為 Promise 進行型別定義,確保每個異步操作的返回值符合預期。讓我們看看如何做到這一點。

1. 基本的 Promise 型別定義

如果我們有一個異步函式,返回一個數字,我們可以這樣定義它的型別:

function fetchNumber(): Promise<number> {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(777);  // 假設我們成功取得了數字 777
    }, 1000);
  });
}

在這裡,我們明確告訴 TypeScript,fetchNumber 函式返回一個 Promise<number>,代表這個異步操作最終會返回一個 number

2. 使用 async/await 定義異步函式型別

asyncawait 是用來簡化異步操作的語法,並且它們在 TypeScript 中可以很輕鬆地和型別結合使用。

讓我們來看看如何為一個使用 async/await 的函式添加型別:

async function getUserName(userId: number): Promise<string> {
  const response = await fetch(`https://api.example.com/users/${userId}`);
  const data = await response.json();
  return data.name;  // 假設 API 返回的資料中包含 name 欄位
}

在這裡,getUserName 函式被定義為返回一個 Promise<string>,這表示它最終會返回一個字串型別的值。在這個範例裡可以清楚知道 API 的回應中 name 是字串,因此 TypeScript 能幫助我們在使用時確保返回的值型別正確。

3. 多種型別的 Promise 處理

有時候,異步操作可能會根據不同情況返回不同型別的值。所以可以使用「聯合型別」來處理這種情況。

假設我們的 API 根據請求結果,會返回一個成功的物件或一個錯誤訊息:

type ApiResponse = { success: true; data: string } | { success: false; error: string };

function fetchApiData(): Promise<ApiResponse> {
  return new Promise((resolve) => {
    setTimeout(() => {
      // 模擬 API 回應
      resolve({ success: true, data: 'Some data' });
    }, 1000);
  });
}

在這個範例中,我定義了一個 ApiResponse 型別,它可以是成功的回應(包含 data)或失敗的回應(包含 error)。當我們處理這個 Promise 時,TypeScript 能協助我檢查回應的結構:

fetchApiData().then((response) => {
  if (response.success) {
    console.log('Data:', response.data);
  } else {
    console.error('Error:', response.error);
  }
});

這樣就可以確保只在回應成功時處理資料,並且在回應失敗時處理錯誤。

三、進階範例:處理 API 錯誤

當處理異步操作時,錯誤處理也是不可忽視的部分。透過 TypeScript,我們可以進階地為錯誤處理加上明確的型別,讓程式碼更具健全性。

假設我們需要處理網絡請求的錯誤:

async function fetchWithErrorHandling(): Promise<string | Error> {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data.content;  // 假設 API 回應有 content 欄位
  } catch (error) {
    return new Error('Failed to fetch data');
  }
}

在這裡,fetchWithErrorHandling 函式可能會返回字串或 Error,這讓我們可以明確地知道這個函式可能失敗。當我們在別的地方調用這個函式時,可以針對不同情況做處理:

fetchWithErrorHandling().then((result) => {
  if (result instanceof Error) {
    console.error(result.message);
  } else {
    console.log('Data:', result);
  }
});

TypeScript 在這裡的作用就是幫助我們清楚辨識異步操作的結果型別,讓錯誤處理更加簡潔與安全。

四、小結

使用 TypeScript 為 Promise 和異步操作添加型別,能大幅提升程式碼的可讀性和穩定性;也可以更清楚地了解每個異步函式返回的型別,避免在處理複雜的 API 回應時出現錯誤。


上一篇
Day23:使用 TypeScript 為 API 請求和響應定義型別
下一篇
Day25:TypeScript 的高級型別 (Advanced Types)
系列文
用 TypeScript 重新定義前端開發:30 天的實踐與思考30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言