# 🏅 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; } ``` ![截圖 2024-01-29 下午11.57.34](https://hackmd.io/_uploads/SyMbiHr5T.png) ### 待辦事項情境 有一個代表待辦事項的介面 **`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 是唯讀的 ``` ![截圖 2024-01-30 上午12.16.39](https://hackmd.io/_uploads/SypdkUS5p.png) **`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 屬性有缺~ ``` ![截圖 2024-01-30 上午12.27.13](https://hackmd.io/_uploads/H1DefUH9a.png) ## 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