Readonly 2
Проблема
Реализовать тип MyReadonly2<T, K>. Тип параметр K содержит список свойств
T, которые должны быть указаны как readonly. В случае, если K не передали
в аргументах, все свойства T должны быть readonly. Например:
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}
const todo: MyReadonly2<Todo, "title" | "description"> = {
  title: "Hey",
  description: "foobar",
  completed: false,
};
todo.title = "Hello"; // Error: cannot reassign a readonly property
todo.description = "barFoo"; // Error: cannot reassign a readonly property
todo.completed = true; // OK
Решение
Эту проблему можно считать продолжением Readonly<T>. Всё
то же, кроме того, что теперь у нас появляется новый тип параметр K, через
который указываем, какие свойства сделать неизменяемыми.
Начнём со случая, когда K это пустое объединение, то есть, ничего не должно
быть неизменяемым. Всё просто - возвращаем T без изменений.
type MyReadonly2<T, K> = T;
Теперь, обработаем случай, когда у нас указаны свойства в K. Используем типы
пересечений и добавим к нашему T новый объект. В этом объекте, используя
сопоставляющие типы, перечислим свойства из K и добавим к ним модификатор
readonly.
type MyReadonly2<T, K> = T & { readonly [P in K]: T[P] };
Выглядит как решение, но получаем ошибку “Type ‘P’ cannot be used to index type
‘T’”. И это правда, у нас нет ограничений на K. Ограничим его до “ключи из
T”.
type MyReadonly2<T, K extends keyof T> = T & { readonly [P in K]: T[P] };
А теперь работает? Нет! Мы забыли о случае, когда K не передан вовсе, как
аргумент. Это тот случай, когда MyReadonly2 ведёт себя как встроенный
Readonly<T>. Добавим к K тип параметр по умолчанию “все ключи из T”.
type MyReadonly2<T, K extends keyof T = keyof T> = T & {
  readonly [P in K]: T[P];
};
Комментарии