Disclosure

Most of the problems under my TypeHero Challenges folder were either obtained from typehero.dev or from type-challenges repo. Purpose of these articles are just to document my approaches for my easy reference. Please visit the respective links for more info.

Link to original

Problem Description

Implement a generic DeepReadonly<T> which make every parameter of an object - and its sub-objects recursively - readonly.

You can assume that we are only dealing with Objects in this challenge. Arrays, Functions, Classes and so on do not need to be taken into consideration. However, you can still challenge yourself by covering as many different cases as possible.

For example:

type X = { 
  x: { 
    a: 1
    b: 'hi'
  }
  y: 'hey'
}
 
type Expected = { 
  readonly x: { 
    readonly a: 1
    readonly b: 'hi'
  }
  readonly y: 'hey' 
}
 
type Todo = DeepReadonly<X> // should be same as `Expected`

Solutions

Approach 1: Checking for Functions

type DeepReadonly<T> = {
	readonly [K in keyof T]: T[K] extends Function ? T[K] : DeepReadonly<T[K]>;
};

Approach 2: Checking for never

type DeepReadonly<T> = {
	readonly [K in keyof T]: keyof T[K] extends never ? T[K] : DeepReadonly<T[K]>;
};

T[K] might be a function and trying to iterate through a function’s keys would produce the wrong type (i.e {}). To combat this, we can utilize the fact that keyof Fn would return never. Which is how the above condition works.