Завдання

Реалізувати тип DeepReadonly<T> який робить властивості об’єкту незмінними (рекурсивно!). Наприклад:

type X = {
  x: {
    a: 1;
    b: "hi";
  };
  y: "hey";
};

type Expected = {
  readonly x: {
    readonly a: 1;
    readonly b: "hi";
  };
  readonly y: "hey";
};

const todo: DeepReadonly<X>; // should be same as `Expected`

Розв’язок

Це завдання схоже з тим, що ми розв’язували в Readonly<T>. Відрізняється тільки тим, що тут потрібно це робити рекурсивно.

Почнемо з класичної реалізації і зробимо Readonly<T>:

type DeepReadonly<T> = { readonly [P in keyof T]: T[P] };

Але, як ви вже здогадалися, цей тип не зробить всі властивості незмінними, а тільки ті, що знаходяться на першому рівні вкладеності. Причина в тому, що якщо T[P] це не примітив, а об’єкт, то його властивості залишаться неопрацьованими.

Тому, замінимо T[P] на рекурсивний виклик DeepReadonly<T>. І оскільки ми вже почали використовувати рекурсію, не забуваємо про базовий випадок. Алгоритм простий. Якщо, T[P] - об’єкт, рухаємося в глибину й викликаємо DeepReadonly, в інакшому випадку — повертаємо T[P] без змін.

type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends Record<string, unknown>
    ? DeepReadonly<T[P]>
    : T[P];
};

Посилання