Вопрос или проблема
Существует ли способ динамически обернуть каждое свойство в интерфейсе в обобщенный тип?
У меня есть несколько интерфейсов, которые определены в библиотеке. Я не могу их изменить (т.е. добавить обобщенные типы, например). Следующий интерфейс является примером одного из возможных интерфейсов, определенных в библиотеке.
interface Original {
a: string;
b: number;
c: boolean;
}
У меня также есть этот обобщенный тип, который я использую для отображения значений в интерфейсе. (Т.е. каждый атрибут интерфейса будет иметь собственный svg-иконку, единицы измерения и т.д.) Я создал этот обобщенный тип сам.
interface GenericInterface<V> {
value: V;
units: string;
errorMsg?: string;
icons: ReactElement<any>;
}
Что я хочу сделать, так это обернуть каждый параметр в интерфейсе Original
в GenericInterface
. В результате я хочу получить следующее:
interface Modified {
a: GenericInterface<string>;
b: GenericInterface<number>;
c: GenericInterface<boolean>;
}
Ближе всего к достижению этого результата я подошел так:
type ModifiedGeneric<T extends object, K extends keyof T> = Record<
K,
GenericInterface<T[K]>
>;
type Modified = ModifiedGeneric<
Original,
keyof Original
>;
Но это приводит к объединению всех типов параметров интерфейса. т.е.
interface Modified {
a: GenericInterface<string | number | boolean>;
b: GenericInterface<string | number | boolean>;
c: GenericInterface<string | number | boolean>;
}
Кто-нибудь знает решение, которое я могу использовать для достижения желаемого результата?
Как указывает jonrsharpe в комментариях, просто используйте отображаемый тип:
interface Original {
a: string;
b: number;
c: boolean;
}
interface GenericInterface<V> {
value: V;
units: string;
errorMsg?: string;
icons: React.ReactElement<any>;
}
type ModifiedGeneric<T extends {}> = {
[key in keyof T]: GenericInterface<T[key]>
}
type ModifiedOriginal = ModifiedGeneric<Original>
// ModifiedOriginal имеет тип
// {
// a: GenericInterface<string>;
// b: GenericInterface<number>;
// c: GenericInterface<boolean>;
// }
Обратите внимание, что второй обобщенный параметр не нужен, если вы не хотите иметь возможность работать с подмножеством keyof T
.
Ответ или решение
В TypeScript существует возможность динамически оборачивать свойства интерфейса с помощью использованию отображаемых типов (mapped types). Это позволяет создать новый интерфейс, где каждое свойство исходного интерфейса будет обернуто в другой тип. Рассмотрим решение для вашей задачи, где мы хотим обернуть каждое свойство интерфейса Original
в интерфейс GenericInterface
.
Пример решения
-
Исходный интерфейс:
Вначале определим интерфейс, который вы получили из библиотеки:interface Original { a: string; b: number; c: boolean; }
-
Общий интерфейс:
У вас уже есть общий интерфейсGenericInterface
, который мы будем использовать:interface GenericInterface<V> { value: V; units: string; errorMsg?: string; icons: React.ReactElement<any>; }
-
Создание нового интерфейса с использованием отображаемых типов:
Теперь давайте создадим новый типModifiedGeneric
, который будет оборачивать каждое свойство интерфейсаOriginal
вGenericInterface
. Для этого воспользуемся синтаксисом отображаемого типа:type ModifiedGeneric<T extends {}> = { };
-
Использование нового типа:
Мы можем теперь создать новый типModifiedOriginal
, который будет соответствовать вашему ожиданию:type ModifiedOriginal = ModifiedGeneric<Original>;
Таким образом,
ModifiedOriginal
будет представлять собой интерфейс, который имеет следующий вид:// Тип ModifiedOriginal будет: // { // a: GenericInterface<string>; // b: GenericInterface<number>; // c: GenericInterface<boolean>; // }
Заключение
Теперь у вас есть полное решение для динамического оборачивания свойств интерфейса с использованием отображаемых типов в TypeScript. Таким образом, вы можете эффективно организовать свои интерфейсы для отображения значений с дополнительной информацией, такой как единицы измерения и иконки, без необходимости изменения исходных интерфейсов, определяемых в библиотеке.
Это решение позволяет вам гибко адаптировать дополнительные интерфейсы, не меняя оригинал, сохраняя при этом типовую безопасность в приложении.