iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0
JavaScript

TypeScript Type Challenges 冒險篇章:30 天闖關之旅,type 簡單了?你確定?系列 第 4

第4關:Readonly!TypeScript 可遠觀不可褻玩焉: Readonly 保護罩

  • 分享至 

  • xImage
  •  

第4關:Readonly

關卡簡介

Implement the built-in Readonly<T> generic without using it.
Constructs a type with all properties of T set to readonly, meaning the properties of the constructed type cannot be reassigned.

實現內建的 Readonly<T> 泛型,而不使用它。
構造一個將 T 的所有屬性設為 readonly 的型別,意味著構造後的型別屬性無法被重新賦值。

任務說明:

interface Todo {
  title: string
  description: string
}

const todo: MyReadonly<Todo> = {
  title: "Hey",
  description: "foobar"
}

todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property

接下來,你的任務是讓下面的type cases測試通過:

type cases = [
  Expect<Equal<MyReadonly<Todo1>, Readonly<Todo1>>>,
]

interface Todo1 {
  title: string
  description: string
  completed: boolean
  meta: {
    author: string
  }
}

冒險指南:

在這一關中,我們可以從以下幾個方向來思考:

  1. 我們需要實現一個泛型類型 MyReadonly
  2. 這個類型會把給定類型 T 的所有屬性都設置為 readonly,確保屬性在初始化後不能被修改,從而達到不可變的效果。

我們將會用到:

  1. Mapped Types:用來遍歷類型的所有屬性並進行修改。
  2. Mapping Modifiers:用來設置屬性為只讀,例如使用 readonly 關鍵字。
Mapped Types 在第三關已經介紹過,可以回到第三關參考喔!

通關方式:

實現 MyReadonly<T> 的關鍵在於使用 TypeScript 的mapped types語法來遍歷 T 的所有屬性並將其設為只讀 readonly

type MyReadonly<T> = {
  readonly [P in keyof T] : T[P]
}

細節分析:

  • type MyReadonly<T> = { ... }: 這定義了一個泛型類型 MyReadonly,它接受一個類型 T,並產生一個新的類型。在這個新的類型中,T 的所有屬性都被設為只讀。

  • readonly [P in keyof T]: 這部分使用 TypeScript 的映射類型語法遍歷 T 的所有鍵(屬性名稱)P。這裡的 keyof T 會獲得 T 所有屬性的聯合類型(例如,如果 T 包含屬性 abkeyof T 會是 'a' | 'b')。

    • [P in keyof T]: 這是 TypeScript 的索引簽名(index signature)的一種用法,表示對 T 中的每個屬性鍵 P 執行操作。在這裡,它的目的是遍歷 T 中的每個屬性鍵,並對它們進行只讀處理。
  • readonly: 這個關鍵字使得映射出的每個屬性都變成只讀屬性。這意味著一旦對這個類型的物件進行了初始化,就不能再改變這些屬性的值。例如,如果你有一個物件 obj,你不能對 objproperty 屬性進行賦值操作。

  • T[P]: 這表示對於每個鍵 P,我們保留 T 中對應屬性的原始類型。也就是說,MyReadonly 類型中的每個屬性的型別和原來 T 中的一樣,只不過現在這些屬性是只讀的。例如,若 T 中有一個屬性 a,它的型別是 string,那麼 MyReadonly<T>a 的型別也會是 string,但它是只讀的。

這樣,我們就能順利通過測試啦 🎉 😭 🎉

關鍵字補給:

  • Readonly<Type> (TypeScript 內建 Utility Type):

    • 用途:構造一個將 Type 的所有 properties (屬性) 設為 readonly 的型別,意味著這些屬性的值在初始化後不能被 reassigned (重新賦值)。
    • 範例:
      interface Todo {
        title: string;
      }
      
      const todo: Readonly<Todo> = {
        title: "Delete inactive users",
      };
      
      todo.title = "Hello"; // Cannot assign to 'title' because it is a read-only property.
      
  • Mapping Modifiers(修飾符):

    • 用途:
      當我們在mapping時,有兩個額外的 modifiers (修飾符) 可以用來調整屬性的 mutability (可變性) 和 optionality (可選性) :readonly?。你可以通過在修飾符前加上 -+ 來添加或移除這些修飾符。如果你沒有加上 prefix (前綴),則默認假設是 +
    • 範例:
      // Removes 'readonly' attributes from a type's properties
      type CreateMutable<Type> = {
        -readonly [Property in keyof Type]: Type[Property];
      };
      
      type LockedAccount = {
        readonly id: string;
        readonly name: string;
      };
      
      type UnlockedAccount = CreateMutable<LockedAccount>; 
          //^? type UnlockedAccount = {
          //   id: string;
          //   name: string;
          //   }
      
      // Removes 'optional' attributes from a type's properties
      type Concrete<Type> = {
        [Property in keyof Type]-?: Type[Property];
      };
      
      type MaybeUser = {
        id: string;
        name?: string;
        age?: number;
      };
      
      type User = Concrete<MaybeUser>;
          //^?  type User = {
          //    id: string;
          //    name: string;
          //    age: number;
          //    } 
      

總結:

本次介紹了 Readonly 的實作,下一關會挑戰Tuple to Object,期待再相見!


上一篇
第3關:Pick!TypeScript 撿寶術:用 Pick 精準撿取型別
下一篇
第5關:Tuple to Object!TypeScript 魔法現眼前:元組變物件
系列文
TypeScript Type Challenges 冒險篇章:30 天闖關之旅,type 簡單了?你確定?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言