Challenge 7

The instructions for this challenge are:

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>.

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:

  1. An object containing all the key/value pairs which have a key in the type of K, each converted to readonly
  2. 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>; 

Blog

Miscellaneous thoughts.