Implement a generic
MyReadonly2<T, K>
which takes two type argument T and K.
K
specify the set of properties of T
that should set to Readonly. When K
is not provided, it should make all properties readonly just like the normal Readonly<T>
.
實現一個泛型 MyReadonly2<T, K>
,它接受兩個型別參數 T
和 K
。
K
用來指定 T
中應設為 Readonly
的屬性集。如果沒有提供 K
,則應將所有屬性設為 readonly
,就像普通的 Readonly<T>
一樣。
interface Todo {
title: string
description: string
completed: boolean
}
const todo: MyReadonly2<Todo, 'title' | 'description'> = {
title: "Hey",
description: "foobar",
completed: false,
}
todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
todo.completed = true // OK
接下來,你的任務是讓下面的type cases測試通過:
type cases = [
Expect<Alike<MyReadonly3<Todo1>, Readonly<Todo1>>>,
Expect<Alike<MyReadonly3<Todo1, 'title' | 'description'>, Expected>>,
Expect<Alike<MyReadonly3<Todo2, 'title' | 'description'>, Expected>>,
Expect<Alike<MyReadonly3<Todo2, 'description' >, Expected>>,
]
interface Todo1 {
title: string
description?: string
completed: boolean
}
interface Todo2 {
readonly title: string
description?: string
completed: boolean
}
interface Expected {
readonly title: string
readonly description?: string
completed: boolean
}
(Alike)
export type MergeInsertions<T> =
T extends object
? { [K in keyof T]: MergeInsertions<T[K]> }
: T
export type Alike<X, Y> = Equal<MergeInsertions<X>, MergeInsertions<Y>>
從以下幾個方向來思考:
屬性篩選與處理 (Property Selection & Handling): 需要從類型 T
中挑選出被指定的屬性 K
,將其設為 readonly
,而其他屬性保持原樣。你可以利用 keyof 來提取 T 的所有屬性,並使用條件型別來篩選屬性。
泛型參數設計 (Generic Parameters): K
是可選的,當 K
未提供時,應將所有屬性設為 readonly,這類行為可以通過給 K
設置預設值來實現。
映射型別 (Mapped Types): 映射型別將幫助我們重新構造 T
,並對不同的屬性應用不同的處理邏輯。需要結合條件型別,對被篩選的屬性應用 readonly
,而對其餘屬性保持不變。
條件型別 (Conditional Types): 使用條件型別來判斷每個屬性是否屬於 K
,從而決定是將其設為 readonly 還是保持原狀。
解法1:
type MyReadonly2<T, K extends keyof T = keyof T> = {
// 先利用never移除不要的:每一個T的P,是否有extends K(要選取的)?
[P in keyof T as P extends K ? never : P]: T[P]
} &
// 再將所選的加上readonly
// readonly [P in keyof T as P extends K ? P : never]: T[P];
// 針對上面這行,我們其實只需要`P extends K ? P`所以可以解化成...`[P in K]`,each key P in the union of keys K
{ readonly [P in K]: T[P] }
解法2:
// type MyReadonly2<T, K extends keyof T = keyof T> = Omit<T,K> & Readonly<Pick<T,K>>
細節分析:
以上達成互補
K
is not provided, it should make all properties readonly just like the normal Readonly<T>
這樣,我們就能順利通過測試啦 🎉 😭 🎉
本次介紹了 Readonly 2
的實作,下一關會挑戰 Deep Readonly
,期待再相見!