我试着定义两种类型,应该是:
export type IQuery<P, U> = {
request: string;
params: (props: P, upsteam?: U) => object;
key: (props: P, upstream?: U) => string;
forceRequest: boolean;
depends?: QueryMap
}
export type QueryMap = {
[k: string]: IQuery
};
我试图表达的约束是,
params
和key
对于它们的两个参数具有相同的类型,而QuyYMAP只是从一个字符串到任意的IQuery
的映射(不管这些类型是什么)。编译器在这里抱怨是因为它希望为IQuery
指定一种类型,但要点是地图中的每个IQuery
应独立参数化。有没有办法用字体来表达?另外,如果可能的话,我想在遍历此树时获得关于
QueryMap
中存在的上游IQuery
s形状的信息/保证。 最佳答案
你能做的最简单的事情是:
export type QueryMap = {
[k: string]: IQuery<any, any>
};
它并不完全是类型安全的,但它与您所要表示的并不太远。如果不想丢失
QueryMap
类型值的类型信息,请允许编译器推断较窄的类型,并使用泛型帮助函数确保它是有效的QueryMap
,如下所示:const asQueryMap = <T extends QueryMap>(t: T) => t;
const queryMap = asQueryMap({
foo: {
request: "a",
params(p: string, u?: number) { return {} },
key(p: string, u?: number) { return "hey" },
forceRequest: true
}
});
值
queryMap.foo.params
仍然是一个接受string
和可选number
的方法,即使类型QueryMap['foo']['params']
不是。如果您指定了一些不可分配给
QueryMap
的内容,您将得到一个错误:const bad = asQueryMap({
foo: {
request: "a",
params(p: string, u?: number) { return {} },
key(p: string, u?: number) { return "hey" },
forceRequest: true
},
bar: {
request: 123,
params(p: number, u?: string) {return {}},
key(p: number, u?: string) {return "nope"},
forceRequest: false
}
}); // error! bar.request is a number
此处显示了不完全类型安全问题:
const notExactlySafe = asQueryMap({
baz: {
request: "a",
params(p: number, u?: string) { return {} },
key(p: string, u?: number) { return "hey" },
forceRequest: true
}
});
这是可以接受的,即使这里没有一致的
P
和U
合理值(这就是使用any
时发生的情况)。如果您需要进一步锁定此值,可以尝试让typescript从该值推断P
和U
值集,如果不能,则警告您,但它不是固定的。为了完整起见,我会这样做…使用conditional types通过检查
P
方法来推断U
和QueryMap
对于params
的每个元素,然后验证key
方法是否与之匹配。const asSaferQueryMap = <T extends QueryMap>(
t: T & { [K in keyof T]:
T[K]['params'] extends (p: infer P, u?: infer U) => any ? (
T[K] extends IQuery<P, U> ? T[K] : IQuery<P, U>
) : never
}
): T => t;
现在,以下操作仍然有效:
const queryMap = asSaferQueryMap({
foo: {
request: "a",
params(p: string, u?: number) { return {} },
key(p: string, u?: number) { return "hey" },
forceRequest: true
}
});
虽然现在这将是一个错误:
const notExactlySafe = asSaferQueryMap({
baz: {
request: "a",
params(p: number, u?: string) { return {} },
key(p: string, u?: number) { return "hey" },
forceRequest: true
}
}); // error, string is not assignable to number
这就增加了你的类型安全性,而牺牲了一个相当复杂的类型——类型的杂耍,所以我不知道它是值得的。
asSaferQueryMap()
在大多数情况下可能已经足够好了。好吧,希望能帮上忙;祝你好运!