任意泛型的打字稿图 [英] Typescript Map of Arbitrary Generics
问题描述
我正在尝试定义两种类型,它们的外观应类似于:
I'm trying to define two types, which should look something like:
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
的两个参数具有相同的类型,而QueryMap只是来自字符串的映射到任意 IQuery
(类型无关).编译器在这里抱怨是因为它希望为 IQuery
指定一种类型,但是要点是,映射中的每个 IQuery
应该独立地参数化.有什么办法可以在打字稿中表达这一点?
The constraints I'm trying to express are that params
and key
have the same types for their two arguments, and that a QueryMap is just a mapping from a string to an arbitrary IQuery
(doesn't matter what the types are). The compiler complains here because it wants a type to be specified for IQuery
, but the point is that each IQuery
in the map should be independently parameterized. Is there any way to express this in typescript?
此外,如果可能的话,我想获取有关遍历此树的 IQuery
中存在的上游 QueryMap
形状的信息/保证.
Additionally, if possible, I'd like to get information/guarantees about the shape of the upstream QueryMap
s present in the IQuery
as I iterated through this tree.
推荐答案
您可以做的最简单事情是这样的:
The simplest thing you can do is this:
export type QueryMap = {
[k: string]: IQuery<any, any>
};
它不是完全类型安全的,但是与您要表示的内容相差不远.如果您不想丢失类型为 QueryMap
的值的类型信息,请允许编译器推断出较窄的类型,并使用通用帮助函数来确保其为有效的 QueryMap
,就像这样:
It's not completely type-safe, but it is not too far off what you're trying to represent. If you don't want to lose type information for a value of type QueryMap
, allow the compiler to infer a narrower type and use a generic helper function to ensure it is a valid QueryMap
, like this:
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']
.
The value queryMap.foo.params
is still known to be a method that accepts a string
and an optional number
, even though the type QueryMap['foo']['params']
isn't.
如果您指定了不能分配给 QueryMap
的内容,则会出现错误:
If you specify something not assignable to a QueryMap
you will get an error:
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
此处显示的不是完全类型安全的问题:
The not-completely type-safe problem is shown here:
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
值的集合,或者如果不能,则警告您,但这不是明智之举.
This is accepted, even though there's no consistent reasonable values of P
and U
that works here (which is what happens when you use any
). If you need to lock this down more, you can try to have TypeScript infer sets of P
and U
values from the value or warn you if it cannot, but it's not staightforward.
出于完整性考虑,这是我的操作方法...使用条件类型通过检查 params来为
QueryMap
的每个元素推断 P
和 U
方法,然后验证 key
方法是否与之匹配.
For completeness, here's how I'd do it... use conditional types to infer P
and U
for each element of your QueryMap
by inspecting the params
method, and then verify that the key
method matches it.
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;
现在,以下内容仍然有效:
Now the following will still work:
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()
的类型,所以我不知道这是值得的. IQuery<任何,任何>
可能足以满足大多数目的.
This increases your type safety marginally at the expense of a fairly complicated bit of type juggling in the type of asSaferQueryMap()
, so I don't know that it's worth it. IQuery<any, any>
is probably good enough for most purposes.
好的,希望能有所帮助;祝你好运!
Okay, hope that helps; good luck!
这篇关于任意泛型的打字稿图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!