我需要能够根据已知类型的另一个参数推断作为参数传递的函数的参数类型。这有点难以解释所以我写了little demo,转载于此:
interface UnaryFunction<In, Out> {
(arg: In): Out
}
interface WithNumber<In, Out> extends UnaryFunction<In, Out> {
num: number
}
function testExtends<Arg, Fn extends UnaryFunction<Arg, any>>(arg: Arg, fn: Fn): Fn {
return fn;
}
function testInferred<Arg, Out>(arg: Arg, fn: UnaryFunction<Arg, Out>): typeof fn {
return fn
}
function mapWithCaller<From, To>(morphism: UnaryFunction<From, To>): WithNumber<From, To> {
return Object.assign((x: From) => morphism(x), {num: 5})
}
// Error: property length does not exist
testExtends('1', mapWithCaller(x => x.length)).num
// Error: property num does not exist
testInferred('1', mapWithCaller(x => x.length)).num
如您所见,使用testExtends
保留了函数的返回类型,而使用testInferred
时,推断出传递函数的参数类型。
我需要能够结合两个世界的优点,这让我发疯,这根本不可能吗?这是一个错误吗?按设计?
答案 0 :(得分:3)
testInferred
的返回类型的问题相当明显 - 它是非泛型的,返回类型固定为UnaryFunction
testExtends
的问题更复杂。这里的根本原因是您需要通过能够为参数提供上下文类型的函数类型将mapWithCaller
的参数设置为上下文类型。
你实际上有一个有点间接的上下文类型,因为mapWithCaller
本身是通用的,但因为提供了testInferred
的另一个参数而且TypeScript可以路由{{1}的预期返回类型回到它的论点,这不是问题。
无论如何,mapWithCaller
在这里工作,因为testInferred
是一个非通用的函数类型,只有一个调用签名。
为什么UnaryFunction<In, Out>
不为此工作?因为根据testExtends
子句中的边界将上下文类型应用于参数是不安全的。考虑这个调用,这是合法的(除了lambda表达式中的错误):
extends
基于法律通用实例化,interface OtherFunction<Out> {
(arg: string | number): Out
}
testExtends<string, OtherFunction<number>>('1', mapWithCaller(x => x.length));
允许将mapWithCaller
传递给其回调。 TypeScript无法真正假设在一个位置给定一个字符串参数,函数表达式的正确推断边界就是这样。
我认为没有办法在这里编写函数声明,以保留您想要的两种行为,但我可能错了。