我正在尝试基于哈希重命名对象中的键,即:
interface Foo {
a: number;
b: number;
c: number;
}
const f: Foo = null!;
// Change the Foo.b prop to Foo.d and keep the same type
const g = rename(f, { b: "d" } as const);
g.a = 1;
// @ts-expect-error b went away
g.b = 2;
// This works but only b/c I hard-coded `number` in `TValue`
g.d = 2;
我认为我很亲密:
// TODO Don't hardcode `number` here, instead get the T[K] type for the "right K" for P
type TValue<T, M extends { [P in keyof T]?: string }, P extends M[keyof M]> = number;
type Replace<T, M extends { [P in keyof T]?: string }> = Omit<T, keyof M> &
{
// K in keyof M where M[K] == P --> use T[K];
[P in Exclude<M[keyof M], undefined>]: TValue<T, M, P>;
};
function rename<T, M extends { [P in keyof T]?: string }>(t: T, mapped: M): Replace<T, M> {
return t as any;
}
但是“我有这个值,即来自M的值” d”的部分,是什么)a)T是什么?[k]“ b”是那个“ d”,b)T是什么[K]类型”使我难以理解。
我发现了其他一些SO问题/答案,例如Rename keys of an object/interface typesafe,但它们在映射中具有诸如string
的硬编码类型,而不是使用T[K]
的原始类型。
创建一个将原始键映射到重命名键和新类型的帮助程序类型,然后提取给定重命名键的类型:
type RenameMap<T> = Partial<Record<keyof T, PropertyKey>>;
type RenamedKeys<T, M extends RenameMap<T>> = {
[K in keyof T]: { name: M[K], t: T[K] }
}[keyof T];
type Replace<T, M extends Partial<Record<keyof T, PropertyKey>>> = Omit<T, keyof M> & {
[K in Exclude<M[keyof M], undefined>]: Extract<RenamedKeys<T, M>, { name: K }>['t']
};
function rename<T, M extends RenameMap<T>>(
t: T, mapped: M
): Replace<T, M> {
return t as any;
}