# 🏅 Day 19 - Utility Types
TypeScript 提供了幾種工具型別(Utility Types),以方便常見的型別轉換。這些工具型別在全域範圍內可用。
## Partial<Type>
**`Partial<Type>`** 會回傳一個新的型別,會將原型別`<Type>`中的所有屬性都設定為可選。
```tsx=
interface num {
x: number;
y: number;
}
type newNum = Partial<num>;
// 會變成 { x?: number; y?: number; }
```

### 待辦事項情境
有一個代表待辦事項的介面 **`Task`**,它包含了多個屬性,如 **`title`**(標題)和 **`dueDate`**(截止日期)。
如果想要實作一個**更新功能**,讓使用者可以只更新待辦事項的部分資訊,而不是必須提供所有資訊。
```tsx
// 定義待辦事項介面
interface Task {
title: string;
dueDate: Date;
}
// 函式允許更新待辦事項的部分資訊
function updateTask(task: Task, fieldsToUpdate: Partial<Task>) {
return { ...task, ...fieldsToUpdate };
}
// 建立一個既有的待辦事項
const task1 = {
title: "閱讀書籍",
dueDate: new Date(2024, 1, 30), // 2024年2月30日
};
// 僅更新待辦事項的標題
const task2 = updateTask(task1, {
title: "參加線上研討會",
});
```
**`Partial<Task>`** 允許建立一個只包含 **`Task`** 介面中某些屬性的物件,對於只更新待辦事項的部分資訊實用性就蠻大的。
這語法在第 15 天保哥的介紹影片中,[20 分鐘](https://youtu.be/SCLKlc6G0_k)處也有提及其優雅之處
## Readonly<Type>
**`Readonly<Type>`** 這個工具型別也會回傳一個新的型別,主要是將`Type` 的所有屬性都變成唯讀。
### 圖書館情境
介面 **`Book`**,它包含了幾個屬性,如 **`title`**(書名)和 **`author`**(作者)。一旦圖書被登記到系統中,假使不希望這些資訊被改變,就很適合這樣用:
```tsx
// 定義一個代表圖書的介面
interface Book {
title: string;
author: string;
}
// 使用 Readonly 將 Book 的所有屬性變成唯讀
type ImmutableBook = Readonly<Book>;
// 登記一本圖書
const book: ImmutableBook = {
title: "深入淺出 TypeScript",
author: "作者名稱",
};
// 修改書名或作者會導致錯誤
// book.title = "新書名"; // 這行會報錯,因為 title 是唯讀的
```

**`Readonly<Type>`** 也可以與 **`Partial<Type>`** 結合,使用來創建一個所有屬性都是唯讀且可選的型別。**適合用在只能初始化一次但部分屬性可能未被賦值的物件**,如下範例程式碼
```tsx=
const temporaryBook: Readonly<Partial<Book>> = { title: "暫定書名" };
// temporaryBook.title = "新書名"; // 這行會報錯,因為 title 是唯讀的
```
## Required<Type>
這個型別將參數型別 **`Type`** 的所有屬性都設為必須,和 **`Partial<Type>`** 正好相反。
### 延續上面的圖書館範例
在某些情境下,我們希望確保所有這些屬性在建立時都能明確提供,來保持數據的完整性,就適合用 `Required<Type>`。
```tsx=
// 圖書介面,故意都設為 ?
interface LibraryBook {
id?: number;
title?: string;
isCheckedOut?: boolean;
}
// 使用 Required 將 LibraryBook 的所有屬性變為必需
type CompleteLibraryBook = Required<LibraryBook>;
// 創建一個完整的圖書資訊,需要提供所有屬性
const book: CompleteLibraryBook = {
id: 1,
title: "深入淺出 TypeScript",
isCheckedOut: false,
};
// 故意建立不完整的書本資訊
// const incompleteBook: CompleteLibraryBook = { id: 2 }; // 這行會報錯,因為 title 和 isCheckedOut 屬性有缺~
```

## Record<Keys, Type>
**`Record<Keys, Type>`** 用來將一種型別的屬性映射到另一種型別。
### 延續前面圖書館範例,記錄各圖書數量
在圖書館系統中,我們可能需要管理圖書館的不同區域和這些區域中的圖書數量。像是特定的圖書區域,例如 "文學"、"科學"、"歷史"。
```tsx
// 定義各區域的圖書數量資訊
interface BookCount {
totalBooks: number;
availableBooks: number;
}
// 定義圖書館區域的類型
type LibrarySection = "文學" | "科學" | "歷史";
// 使用 Record 建立一個物件型別
// key 為 LibrarySection
// Type 為 BookCount
const sectionBookCounts: Record<LibrarySection, BookCount> = {
"文學": { totalBooks: 100, availableBooks: 80 },
"科學": { totalBooks: 150, availableBooks: 120 },
"歷史": { totalBooks: 90, availableBooks: 85 },
};
// 存取特定區域的圖書數量資訊
const literatureSection = sectionBookCounts["文學"];
//literatureSection 的 type 為 BookCount
//literatureSection 的 value 為 { totalBooks: 100, availableBooks: 80 }
```
## Pick<Type, Keys>
**`Pick<Type, Keys>`** 用途在要從一個型別 **`Type`** 中選取一組屬性 **`Keys`**(字串字面量、字串字面量+聯合型別)來建立一個新型別。
### 僅需顯示圖書的簡略資訊
假設有一個圖書介面 **`LibraryBook`**,屬性有 **`id`**、**`title`**、**`author`** 和 **`isCheckedOut`**(圖書是否被借出)。
在某些情況下,可能只需要從這個介面中選部分屬性,來顯示圖書的簡略資訊,而不是顯示所有細節時,就蠻適合使用`Pick<Type, Keys>`,來檢視以下範例:
```tsx
// 圖書介面
interface LibraryBook {
id: number;
title: string;
author: string;
isCheckedOut: boolean;
}
// 使用 Pick 從 LibraryBook 中選取 'title' 和 'isCheckedOut' 屬性來建立一個新型別
type BookPreview = Pick<LibraryBook, "title" | "isCheckedOut">;
// 建立一個圖書的簡略資訊
const bookPreview: BookPreview = {
title: "深入淺出 TypeScript",
isCheckedOut: true,
};
// bookPreview 只包含圖書的標題和借閱狀態,適合用於圖書借閱狀態的快速查看
```
## 小結
以上挑選相關工具型別,主要是很適合用在於 `interface`、`type` 的使用靈活性上能夠更具彈性。
## 問題
請挑選以下自己不熟悉的 Utility Type ,並嘗試撰寫程式碼來進行解讀,附上其它沒介紹到的:
1. Omit<Type, Keys>
1. Exclude<UnionType, ExcludedMembers>
1. Extract<Type, Union>
1. InstanceType<Type>
1. NonNullable<Type>
1. OmitThisParameter<Type>
1. Parameters<Type>
1. ReadonlyArray<Type>
1. ReturnType<Type>
1. ThisParameterType<Type>
1. ThisType<Type>
1. OrNull<Type>
1. OneOrMany<Type>
1. OneOrManyOrNull<Type>
1. ConstructorParameters<Type>
1. Awaited<Type>
## 回報區
| Discord | CodePen / 答案 | 挑選的工具型別 |
|:-------------:|:----------------------------------------------------------------:|:----------------------------------------------------------------:|
|洧杰|[Codepen](https://codepen.io/hexschool/pen/poYgYqW?editors=1010)| Pick、ReturnType
|HsienLu|[Codepen](https://www.typescriptlang.org/play?#code/C4TwDgpgBAhgdgSwLYwDYF4BQAfARAQQBMB7Ac1x1wCETzKBhAYxmArwBEwE7Hi4BnYFABexYgC4AogA9GqAK6EIAHnjI0AGgAGAEgDeggE4I4pAL5dSWgHwBtALrpbBWrg3VX7pize5O3XHtMXgFiVAgAOlQyAApRYgBKTCA)|Exclude
|苡安|[Codepen](https://stackblitz.com/edit/typescript-bgaf14?file=index.ts)| Omit、Extract、Parameters
|hannahpun|[Blog](https://medium.com/hannah-lin/typescript-%E7%94%A8%E7%94%9F%E6%B4%BB%E4%BE%8B%E5%AD%90%E5%9C%96%E8%A7%A3-utility-type-2eeee27a58cd#f2be)| Parameters、ReturnType、Awaited、String Manipulation Types