Я собираюсь ответить на общий вопрос о том, как программно изменить тип объекта для переименования ключей.
Вот одна из возможных реализаций, которая действительно работает только для требуемых, изменяемых свойств с известными литеральными ключами (не обязательносвойства, без readonly
свойств и без подписи индекса).Если вам нужно поддерживать эти случаи, это возможно, но становится еще более уродливым, поэтому я пока игнорирую это.
type ValueOf<T> = T[keyof T];
type RenameKeys<T, M extends Record<keyof any, keyof any>> = ValueOf<
{ [K in keyof T]: (x: Record<K extends keyof M ? M[K] : K, T[K]>) => void }
> extends ((x: infer R) => void)
? { [K in keyof R]: R[K] }
: never;
Это может сбить с толку, но в основном это требует тип объекта T
итип сопоставления клавиш M
и создает новый тип с переименованными ключами, но типы свойств совпадают.Например:
interface MyModel {
id: number;
name: string;
age: number;
alive: boolean;
}
type Renamed = RenameKeys<MyModel, { id: "uuid" }>;
производит
type Renamed = {
uuid: number;
name: string;
age: number;
alive: boolean;
}
и
type RenamedMulti = RenameKeys<
MyModel,
{ id: "uuid"; alive: "notDead"; age: "yearsOld" }
>;
производит
type RenamedMulti = {
uuid: number;
name: string;
yearsOld: number;
notDead: boolean;
}
Возможно, вы сможете использовать RenameKeys
создать типы, которые вы ищете.
Как это работает:
type RenameKeys<T, M extends Record<keyof any, keyof any>> = ValueOf<
{ [K in keyof T]: (x: Record<K extends keyof M ? M[K] : K, T[K]>) => void }
> extends ((x: infer R) => void)
? { [K in keyof R]: R[K] }
: never;
Расширение отображения M
Record<keyof any, keyof any>
просто гарантирует, что это карта от ключей к ключам.Тогда давайте представим это: {[K in keyof T]: Record<K extends keyof M ? M[K] : K, T[K]>]}
.Это в основном берет каждое свойство в T
и ищет в M
, чтобы увидеть, сопоставлен ли ключ, и если да, отображает его ... Если вы сделали это, где T
было MyModel
и M
было{id: "uuid"}
, тогда вы получите {id: {uuid: number}, name: {name: string}, age: {age: number}, alive: {alive: boolean}}
.
Немного сложно перейти от этого к типу с этими объединенными ... я делаю это с выводом условного типа , чтобы превратить объединение этих типов в пересечение.Сначала я помещаю эти свойства в параметры функции, например (x: Record<...> => void)
.Затем я получаю объединение функций (это приложение ValueOf<>
) и выводю из него одну функцию с типом параметра R
.Это заканчивается пересечением, поскольку (x: A)=>void | (x: B)=>void
присваивается (x: A & B) => void
.(Другое объяснение того, как это работает, здесь или, возможно, здесь )
Таким образом, тип R
, который получается, выглядит как {uuid: number} & {name: string} & {age: number} & {alive: boolean}
, а затем {[K in keyof R]: R[K]}
является сопоставленным типом «identity», который объединяет эти свойства в {uuid: number; name: string; age: number; alive: boolean}
.
Хорошо, надеюсь, это поможет;удачи!
Ссылка на код