打字稿:传递函数参数的推断类型

时间:2018-05-23 00:09:46

标签: typescript generics

我需要能够根据已知类型的另一个参数推断作为参数传递的函数的参数类型。这有点难以解释所以我写了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时,推断出传递函数的参数类型。

我需要能够结合两个世界的优点,这让我发疯,这根本不可能吗?这是一个错误吗?按设计?

1 个答案:

答案 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无法真正假设在一个位置给定一个字符串参数,函数表达式的正确推断边界就是这样。

我认为没有办法在这里编写函数声明,以保留您想要的两种行为,但我可能错了。