---
# System prepended metadata

title: "\U0001F3C5 Day 17 - Promise 非同步設計"
tags: [TypeScript 30 天證書培訓營 - 2024]

---

# 🏅 Day 17 - Promise 非同步設計


我們在開發時，很常會需要使用到非同步技術，來開發前後端功能，所以今天來分享一下 promise 吧 :D

當在 TypeScript 中使用 Promise 時，你可以透過泛型來指定 Promise 被解析出來的型別。藉此獲得型別提示跟檢查。

### promise 情境

當我們寫一個 promise 時，會寫成如下程式碼：

```=tsx
const promise = new Promise((resolve) => {
    setTimeout(() => {
        resolve('hello!');
    }, 2000);
});


promise.then((data) => {
    data.split('');
});
``` 
當我們滑鼠滑到 `resolve` 參數時，會被告知，目前他的型別為 `unknown`

![截圖 2024-01-29 下午8.52.58](https://hackmd.io/_uploads/SJET1mH5p.png)

那麼 `unknown` 是什麼呢？

## 小教室：unknown 是什麼？又跟 any 有什麼差別

`unknown` 型別也是任意值的意思，那麼和 `any` 的差別在哪呢？

在使用 `unknown` 型別進行屬性方法操作時，必須在那之前進行型別檢查或斷言才會正常，如下程式碼：

```=tsx

// 使用 any 型別
let valueAny: any = "Hello, world!";
console.log(valueAny.length); // 正常

// 使用 unknown 型別
let valueUnknown: unknown = "Hello, world!";

// 嘗試直接訪問 unknown 型別會導致編譯錯誤
// console.log(valueUnknown.length); // 編譯錯誤
// 會告知型別“unknown”上不存在屬性“length”

// 進行型別檢查
if (typeof valueUnknown === "string") {
    console.log(valueUnknown.length); 
    // 經過型別檢查後才會允許讀取方法
}

```

所以相較而言來說，`unknown` 嚴謹性較高，以下提供比較表格


| 特性 | any | unknown |
| --- | --- | --- |
| 安全性 | 低 (無限制) | 高 (受限制) |
| 型別檢查需求 | 無需型別檢查 | 需要型別檢查或斷言 |
| 操作限制 | 無限制 | 受限，直到型別被確認 |
| 維護性和可讀性 | 較低 | 較高 |


## 加入型別註釋 promise 泛型來優化

```tsx=
const promise: Promise<string> = new Promise((resolve) => {
    setTimeout(() => {
        resolve('hello!');
    }, 1000);
});
```

此時滑鼠滑過去 `resolve` 參數裡面就會被定義為字串型別



### 情境：使用 fetch API 來獲得使用者資訊

假設我們要從一個 API 取得一些使用者資料，這個資料可以用一個介面來描述：

```tsx=
interface User {
  id: number;
  name: string;
}

```

當一個函式需要回傳一個 Promise 時，寫法如下，解析後的型別則定義是 **`User`** 介面，來提升可讀性：

```tsx
function getUser(id: number): Promise<User> {
  return fetch(`https://someapi.com/users/${id}`)
    .then(response => response.json())
    .then(data => data);
}

```

## **結合 async 與 await**

當使用 **`async`** 和 **`await`** 時，TypeScript 能夠從你 **`await`** 的表達式中推斷出回傳值的型別。

### 範例程式碼：

將剛才的 **`getUser`** 函式在另一個 async 函式中使用：

```tsx=

interface User {
  id: number;
  name: string;
}

function getUser(id: number): Promise<User> {
  return fetch(`https://someapi.com/users/${id}`)
    .then(response => response.json())
    .then(data => data);
}

async function showUserName(id: number) {
  const user = await getUser(id);
  // user 會顯示回傳 User 介面
  console.log(`使用者名稱：${user.name}`);
}


```

藉由我們定義了 Promise 泛型後，滑鼠指向 14 行的 user 變數時，型別檢查器就會告知該型別為 `User` 介面，並讓後續的程式碼享有型別檢查的好處，例如在訪問 `user.name` ，也能知道裡面定義的型別是字串型別

![截圖 2024-01-29 下午9.58.42](https://hackmd.io/_uploads/SkDOyVBcp.png)

![截圖 2024-01-29 下午10.02.46](https://hackmd.io/_uploads/r1jzlVBcT.png)




# 開發題

請隨意挑選 TodoList 裡面任一個 API，來加上 Promise 泛型，例如註冊 API、todo API，可參考 Day 16 [苡安同學](https://stackblitz.com/edit/vitejs-vite-qjrf7o?file=src%2Fstores%2FTodoStore.ts%3AL12)的 Promise API 請求搭配 `Todo[]`

1. API 網址：https://todolist-api.hexschool.io/
2. API 文件：https://todolist-api.hexschool.io/doc/




回報區
---


|    Discord    |                          CodePen / 答案                          |
|:-------------:|:----------------------------------------------------------------:|
|洧杰|[Codepen](https://codepen.io/hexschool/pen/poYgYqW?editors=1010)|
|HsienLu|[Codepen](https://codepen.io/Hsienlu/pen/QWoaXKo?editors=1011)|
|苡安|[Codepen](https://codepen.io/yi-an-yang/pen/zYbpVRK)|
|展誠|[CodePen](https://codepen.io/hedgehogkucc/pen/jOJZOgP?editors=1012)|
|shaokang|[CodePen](https://stackblitz.com/edit/vitejs-vite-3hq2pq?file=src%2Fmain.tsx,src%2Findex.css,src%2FSignUp.tsx,src%2FSignUp.css&terminal=dev)|
|hannahTW|[CodePen](https://codepen.io/hangineer/pen/zYbRqvM?editors=0011)|
|wei|[codePen](https://codepen.io/jweeei/pen/RwdQaGr?editors=1010)|
|erwin阿瀚|[CodePen](https://codepen.io/yohey03518/pen/OJqvdXy?editors=1111)
|claire chang|[CodePen](https://codepen.io/claire-chang-the-bashful/pen/OJqvqQQ)
|rikku1756|[CodePen](https://codepen.io/rikkubook/pen/BabVzMJ)