Deep Readonly

Challenge

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`

Solution

Auch hier nutzen wir wieder einen Mapped Typ. Dabei laufen wir wieder über alle Eigenschaften des Objektes und setzen jedes auf readonly. Die einzige Besonderheit ist, dass wir prüfen müssen, ob es sich bei dem Wert der aktuellen Eigenschaft um ein weiteres Objekt handelt. Falls dies zutrifft, rufen wir den Typen einfach rekursiv auf, um somit alle Eigenschaften mit dem readonly-Schlüsselwort zu versehen.

type DeepReadonly<T extends Record<string, any>> = {
  readonly [key in keyof T]: T[key] extends Function
    ? T[key]
    : // Ist T[Key] ebenfalls ein Objekt, so rufen wir den Typen rekursiv erneut auf
    T[key] extends Record<string, any>
    ? DeepReadonly<T[key]>
    : T[key];
};

References