我正在尝试构建一个类似Map的类,该类将一种类型的键映射到另一种类型的键。一个简单的解决方案是仅创建一个Map<keyof A, keyof B>
并将其调用完成。但这不会检查A
和B
中每个键的成员类型是否匹配。
我开始了以下课程,我希望做一些类型断言,证明T
不是never
。
class KeyMap<A, B> {
private mapping = new Map<keyof A, keyof B>();
add<KA extends keyof A, KB extends keyof B, T = A[KA] & B[KB]>(
a: KA,
b: KB
): void {
this.mapping.set(a, b);
}
}
我不知道这是否可能,但是我可以想象这将是一个有用的地图。
要退后一步,我正在尝试构建一个可重用的实用程序,该实用程序将以类型安全的方式在A
和B
之间进行转换,以验证所有成员类型是否匹配。如果有更好的方法可以做到这一点,那么我愿意接受其他建议。
答案 0 :(得分:1)
抛开最后一个“是否有更好的方法来解决这个问题”,这就是我要如何使add()
要求将b
参数限制为仅来自{{1}的键},其中B
参数中A
中的对应属性有一些重叠(例如,a
不是A[KA] & B[KB]
):
never
type KeysWithOverlap<T, V> = { [K in keyof T]-?: (T[K] & V) extends never ? never : K }[keyof T];
class KeyMap<A, B> {
private mapping = new Map<keyof A, keyof B>();
add<KA extends keyof A, KB extends KeysWithOverlap<B, A[KA]>>(
a: KA,
b: KB
): void {
this.mapping.set(a, b);
}
}
接受类型KeysWithOverlap<T, V>
和值类型T
,并返回其属性与V
重叠的T
的所有键。然后,我们要做的就是将V
约束为KB
,而不是约束keyof B
。让我们看看它是如何工作的:
KeysWithOverlap<B, A[KA]>
请注意,由于interface Foo {
a: string;
b: 3;
c: boolean;
}
interface Bar {
d: "str";
e: number;
f: boolean;
}
const km = new KeyMap<Foo, Bar>();
km.add("a", "e"); // error! e is not assignable to d
km.add("a", "d"); // okay
km.add("b", "e"); // okay
km.add("c", "f"); // okay
和add("a", "e")
没有重叠,因此在尝试呼叫string
时会出现错误。其他调用起作用,因为类型兼容。
那么有更好的方法吗?
也许; “没有重叠”可能不够严格;您是否在寻找可以从number
中读取A[KA]
的类型?还是您写一个的人?给定上面的B[KB]
和Foo
类型,如果将Bar
映射到Foo
或从Bar
映射Bar["d"]
会发生什么问题,因为"str"
仅接受特定的字符串"a"
,如果您尝试仅将Foo
的{{1}}属性复制到它,则需要小心。
如果要尝试创建通用的属性映射函数,则可能会在类型系统中表示特定键映射的地方使用它,例如,declare function convert<T, M extends {[K in keyof T]: PropertyKey}>(obj: T, keyMap: M): ConvertKeys<T, M>;
用于{{1}的适当定义}。我也许可以写下来,但这会付出更多的努力,并且从我认为这个问题的重点出发。