我正在玩以下代码段。当我删除行validate: (...) => {}
时,A
被正确地推断为rule1: { v: number }
。但是,如果未删除该行,则将A
推断为unknown
。有什么想法会导致这种情况,以及如何解决?在此先感谢:)
type Arguments<A> = {
[K in keyof A]: {
args: {
[K1 in keyof A[K]]: {
assert?: (arg: unknown) => arg is A[K][K1];
}
};
validate?: (value: unknown, args: A[K]) => unknown;
}
}
function test<A>(args: Arguments<A>) {}
test({
rule1: {
args: {
v: {
assert: (arg): arg is number => typeof arg === 'number',
}
},
validate: (value, args) => {
}
}
});
答案 0 :(得分:1)
这里的问题是,编译器需要在同一个对象内同时推断出泛型类型参数和未注释的回调参数的类型,并且它实际上不能始终如一地做到这一点。这是已知的设计限制;参见microsoft/TypeScript#12621和microsoft/TypeScript#26418等。简短的答案是您要么必须放弃这些推论之一(通过手动指定泛型类型参数或通过手动注释回调参数),要么或将单个对象分开分成多个对象,以便编译器可以分阶段进行多次推理。
我将在此处介绍的解决方法是一个帮助程序函数,该函数可以从两个单独的对象中构建您的单个对象。第一个对象将用于推断通用类型参数,然后将其用于第二个对象中的回调参数的上下文类型。 (我在评论中称此为“ currying”,但实际上不是currying,对术语错误感到抱歉)
const arg = function <T>(
args: { [K in keyof T]: {
assert?: (arg: unknown) => arg is T[K];
} },
validate?: (value: unknown, args: T) => unknown
) {
return ({ args, validate });
}
arg()
函数采用两个参数args
和validate
,并将它们组合为一个对象。现在,调用test()
时,可以使用arg()
来构建每个属性:
test({
rule1: arg({
v: {
assert: (arg): arg is number => typeof arg === 'number',
}
}, (value, args) => { }
)
});
此处,arg()
中的类型参数被推断为{v: number}
,因此现在可以根据需要正确地推断test()
中的类型参数为{rule1: {v: number}}
。这不是完美的,但是这是我目前能想到的最好的。好吧,希望能有所帮助;祝你好运!