Javascript 任意泛型的Typescript映射
我试图定义两种类型,它们应该类似于:Javascript 任意泛型的Typescript映射,javascript,reactjs,typescript,generics,Javascript,Reactjs,Typescript,Generics,我试图定义两种类型,它们应该类似于: 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: st
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
};
导出类型IQuery={
请求:字符串;
参数:(道具:P,团队?:U)=>对象;
键:(道具:P,上游?:U)=>字符串;
forceRequest:布尔;
取决于:查询映射
}
导出类型查询映射={
[k:字符串]:iquiry
};
我试图表达的约束条件是,params
和key
的两个参数具有相同的类型,而QueryMap只是从字符串到任意IQuery
的映射(不管类型是什么)。编译器在这里抱怨,因为它希望为IQuery
指定一个类型,但问题是映射中的每个IQuery
都应该独立参数化。有没有办法用打字脚本来表达这一点
此外,如果可能的话,我希望在遍历此树时获得上游
QueryMap
中存在的IQuery
形状的信息/保证。您可以使用IQuery
我不确定你在问题的第二部分希望得到什么。TypeScript不提供运行时类型信息。如果您只想在操作单个IQuery
时引用类型变量,可以将IQuery
传递给函数myFunction(IQuery:IQuery){…}
您可以做的最简单的事情是:
export type QueryMap = {
[k: string]: IQuery<any, any>
};
值queryMap.foo.params
仍然是一个接受字符串和可选数字的方法,即使类型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 = 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
不完全类型安全问题如下所示:
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
值集,或者在不能的情况下向您发出警告,但这不是直截了当的
为了完整起见,我会这样做。。。用于通过检查params
方法推断QueryMap
中每个元素的P
和U
,然后验证键
方法是否匹配
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 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 = 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
对于大多数用途来说可能已经足够好了
好吧,希望这会有帮助;祝你好运 解决方案
为了清楚起见,我从你的类型中删除了不相关的信息。解决方案归结为基本上添加3行代码
类型检查=查询映射
导出类型IQuery={
prop1:(参数1:P,参数2?:U)=>数字;
prop2:(param1:P,param2?:U)=>字符串;
prop3?:TQueryMap
}
导出类型查询映射={
[K in keyof T]:T[K]
};
//类型构造函数
常量asQueryMap=(x:QueryMap)=>x
常量asQuery=(x:IQuery)=>x
考虑
所有类型都由编译器正确推断
重要:如果(仅当)使用<代码>类型构造函数< /代码>(见上文)来构造您的结构,则可以认为自己完全静态类型安全。
以下是测试用例:
测试无编译错误
多谢各位
干杯这很有帮助。关于条件类型和使用推断关键字的事情或多或少就是我想要的。我应该说,这些地图将是预定义的(它们是整个应用程序中使用的查询),所以我可以让它们自己输入;我只是想让linter/编译器在我进行错误查询时大叫。我还想保证某些函数返回的形状,即getResponse(QueryMap)
的返回形状等于map和上游map中的所有键。太好了,谢谢你!
// Ok -- No compile-time error and correctly infered !
const queryMap = asQueryMap({
a: asQuery({
prop1: (param1: string, param2?: number) => 10,
prop2: (param1: string, param2?: number) => "hello",
}),
b: asQuery({
prop1: (param1: string, param2?: string) => 10,
prop2: (param1: string, param2?: string) => "hello",
}),
c: asQuery({
prop1: (param1: Array<number>, param2?: number) => 10,
prop2: (param1: Array<number>, param2?: number) => "hello",
})
})
const query = asQuery({
prop1: (param1: string, param2?: number) => 10,
prop2: (param1: string, param2?: number) => "hello",
prop3: queryMap
})
// Ok --> Compile Error: 'prop2' signature is wrong
const queryMap2 = asQueryMap({
a: asQuery({
prop1: (param1: Array<string>, param2?: number) => 10,
prop2: (param1: Array<number>, param2?: number) => "hello",
})
})
// Ok --> Compile Error: 'prop3' is not of type QueryMap<any>
const query2 = asQuery({
prop1: (param1: string, param2?: number) => 10,
prop2: (param1: string, param2?: number) => "hello",
prop3: 10 // <---- Error !
})