Readonly 2
Завдання
Реалізувати дженерик MyReadonly2<T, K>
, що приймає два типи-аргументи T
та
K
.
Тип K
визначає множину властивостей з T
, які мають стати readonly
. Якщо
K
немає, він має зробити readonly
всі властивості, як звичайний
Readonly<T>
. Наприклад:
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
, щоб ми могли вказати,
які конкретно властивості мають стати readonly
.
Почнемо з найпростішого рішення. Розглянемо випадок, коли K
є порожньою
множиною, тож нам не потрібно робити щось readonly
. Просто повертаємо T
:
type MyReadonly2<T, K> = T;
Тепер потрібно врахувати випадок, коли K
містить якісь властивості. Ми можемо
використати оператор &
і створити
перетин двох типів:
першим є наш тип T
, а другим є множина властивостей, які треба зробити
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
зовсім немає. Це той самий
випадок, коли наш тип має поводитись так само як Readonly<T>
. Щоб це
виправити, ми просто вкажемо, що за замовчуванням K
, це “всі ключі з T
”:
type MyReadonly2<T, K extends keyof T = keyof T> = T & {
readonly [P in K]: T[P];
};
Коментарі