Самый простой подход - инвертировать параметр типа U
для вашего ReplaceKeys
:
type PossibleKeys = number | string | symbol;
type ReplaceKeys<T extends {}, U extends Record<PossibleKeys, keyof T>> = Omit<T, ValueOf<U>> & {
[K in keyof U]: T[U[K]]
};
Который затем можно использовать следующим образом:
type ReplacedUser = ReplaceKeys<MyInterface, { total: 'propToReplace', items: 'anotherPropToReplace' }>;
Если вы не может изменить форму объекта U
, становится немного сложнее:
// Example types from your post
interface Item {
readonly description: string;
readonly id: string;
}
interface MyInterface {
readonly id: string;
readonly propToReplace: number;
readonly anotherPropToReplace: readonly Item[];
}
// All possible key types
type PossibleKeys = number | string | symbol;
// Helper type to get all non-nullable values from type T
type DefinedValues<T> = NonNullable<T[keyof T]>;
// Helper type for your replacements object - a record whose values are valid keys
// and whose keys are also present in type T (the input type)
//
// Partial is used to make sure you don't need to pass all keys of T in your replacements
type Replacements<T extends {}> = Partial<Record<keyof T, PossibleKeys>>;
// Helper type that swaps object keys for values
type Invert<T extends Replacements<C>, C extends {} = {}> = {
[N in DefinedValues<T>]: {
[K in keyof T]: N extends T[K] ? K : never
}[keyof T]
}
type ReplacedKeys<T extends {}, R extends Replacements<T>> = Omit<T, keyof R | DefinedValues<R>> & {
[N in keyof Invert<R>]: {
[L in keyof R]: N extends R[L] ? (L extends keyof T ? T[L] : never) : never;
}[keyof R]
}
Имейте в виду, что использование второго подхода не предупреждает вас о дублировании сопоставлений:
type ReplacedUser = ReplacedKeys<MyInterface, { propToReplace: 'total', anotherPropToReplace: 'total' }>;
Проверьте игровую площадку здесь .