The instructions for this challenge are:
Implement a generic
MyReadonly2<T, K>
which takes two type argumentT
andK
.
K
specify the set of properties ofT
that should set to Readonly. WhenK
is not provided, it should make all properties readonly just like the normalReadonly<T>
.
Example code that should compile correctly is provided:
interface Todo {
title: string
description: string
completed: boolean
}
const todo: MyReadonly2<Todo, 'title' | 'description'> = {
title: "Hey",
description: "foobar",
completed: false,
}
The initial type is type MyReadonly2<T, K> = any
.
This challenge is not too difficult. We can approach this as two smaller problems using a union type. The resulting type should be a union of:
- An object containing all the key/value pairs which have a key in the type of
K
, each converted toreadonly
- An object containing all the key/value pairs which have a key not in the type of
K
These both begin with either [P in keyof T as P extends K ? P : never]: T[P]
or the inverse. After this we just need to
apply +readonly
to the inclusive type.
The eventual solution is:
type MyReadonly2<T, K extends keyof T = keyof T> = {
+readonly [P in keyof T as P extends K ? P : never] : T[P]
} & {
[P in keyof T as P extends K ? never : P] : T[P]
};
It’s worth noting that this is essentially a union type of our Omit
and Pick
implementations from earlier. As a result
we could write this more simply by using those utility types.
type MyReadOnly2<T, K extends keyof T = keyof T> = Readonly<Pick<T, K>> & Omit<T, K>;