Append to Object
Завдання
Реалізуйте тип, що додає нове поле до інтерфейсу. Тип приймає три аргументи. Повертає об’єкт з доданим полем.
Наприклад:
type Test = { id: "1" };
type Result = AppendToObject<Test, "value", 4>; // expected to be { id: '1', value: 4 }
Розв’язок
Коли ми хочемо змінити об’єкти/інтерфейси в TypeScript, зазвичай для цього
бувають корисні типи перетину (intersection types). Ця задача не виняток. Я
спробував написати тип, що приймає T
та об’єкт з новою властивістю:
type AppendToObject<T, U, V> = T & { [P in U]: V };
На жаль, це рішення не задовольняє тести. Вони очікують плоский тип, а не
перетин. Тому нам необхідно повернути об’єкт, в якому є всі властивості разом з
нашою новою. Почнемо з того, що повернемо властивості T
:
type AppendToObject<T, U, V> = { [P in keyof T]: T[P] };
Тепер нам потрібно додати до властивостей T
нову властивість U
. Для цього ми
можемо передати об’єднання типів оператору in
:
type AppendToObject<T, U, V> = { [P in keyof T | U]: T[P] };
Таким чином ми отримаємо всі властивості з T
разом з властивостями U
, саме
те, що нам потрібно. Виправимо незначні помилки обмеживши U
:
type AppendToObject<T, U extends string, V> = { [P in keyof T | U]: T[P] };
Останнє, що TypeScript не може гарантувати - це те, що P
може не бути в T
,
бо P
- об’єднання T
і U
. Нам необхідно додати перевірку: якщо P
з T
-
отримаємо T[P]
, в іншому випадку V
:
type AppendToObject<T, U extends string, V> = {
[P in keyof T | U]: P extends keyof T ? T[P] : V;
};
Коментарі