【问题标题】:How to parameterise a functions argument tuple in Typescript如何在 Typescript 中参数化函数参数元组
【发布时间】:2015-05-11 06:42:54
【问题描述】:

使用 Typescript 1.4,假设我有一个带有签名的高阶函数:

interface F<A,B> {
    (a: (...x:any[]) => A): (...x:any[]) => B
}

函数“F”采用函数“a”,该函数具有一组参数“x”。函数“F”返回一个与函数“a”具有完全相同的参数集的新函数。

我一直在尝试找到一种在 Typescript 中表达这一点的方法,但我做不到。例如:

interface F<X extends Array<any>,A,B> {
    (a: (...x:X) => A): (...x:X) => B
}

编译器只是抱怨:error TS2370: A rest parameter must be of an array type.

虽然这感觉不对,即使它确实编译了。我想我真的需要这样的东西:

interface F<X extends Tuple,A,B> {
    (a: (...x:X) => A): (...x:X) => B
}

有人知道目前 Typescript 是否可以实现这种事情(撰写本文时为 1.4)?或者有什么建议?


示例:

(注意:这不是我的实际用例,我只是在这里使用日志作为一个简单的例子——请不要专注于这方面)

// a higher-order function that takes any function and
// returns a new function which takes the same args
// passing them to the original fn and logging the args along with the result

function f(a:(...x:any[]) => any): (...x:any[]) => void {
    return (...x:any[]) => {
        console.log('('+x.join(',')+') => ', a.apply(undefined, x));
    }
}

function a(j:string, k:number): boolean {
    return j === String(k);
}

var b = f(a);

b("1", 1);
b("a", 2);

控制台输出:

(1,1) =>  true
(a,2) =>  false

所以这可行,但派生函数“b”具有以下隐含签名:

(...x:any[]) => void

理想情况下,我希望它具有与函数“a”相同的参数,即:

(j: string, k: number) => void

我知道我可以明确地定义它,但它非常冗长而且一点也不理想,有点违背了首先拥有强类型的意义:

var b: (j:string, k:number) => void = f(a);

【问题讨论】:

    标签: generics functional-programming typescript


    【解决方案1】:

    目前无法在 TypeScript 类型系统中表达这一点。这是一个长期存在的问题,您可以关注 https://github.com/Microsoft/TypeScript/issues/212https://github.com/Microsoft/TypeScript/issues/5453

    【讨论】:

      【解决方案2】:

      要使用具有不同返回类型的另一个函数包装一个函数,重载可能是一种技巧:

      function wrap<R>(fn0: ()=> any, p:(f,a)=>R): () => R;
      function wrap<R,T>(fn1: (t:T)=> any, p:(f,a)=>R): (t:T) => R;
      function wrap<R, T, U>(fn2: (t:T, u:U)=> any, p:(f,a)=>R): (t:T,u:U) => R;
      function wrap<R, T, U, V>(fn3: (t:T, u:U, v:V)=> any, p:(f,a)=>R): (t:T,u:U, v:V) => R;
      function wrap<R, T, U, V, X>(fn4: (t:T, u:U, v:V, x:X)=> any, p:(f,a)=>R): (t:T,u:U, v:V, x:X) => R;
      // ...
      function wrap<R>(fn: Function, proc:Function):Function {
      	return (...args) => proc(fn, args);
      }
      
      // wrap is called with two functions fn and proc
      // result is a function with argument types from fn and return type of proc 
      
      function serialize(fn, args):string {
        return JSON.stringify(fn(...args))
      }
      
      function foo(a:number,b:string) {
        return true;
      }
      
      var wrapped = wrap(foo,serialize)
      // type is (a:number,b:string) => string

      小心,它只适用于参数数量有限的函数。

      【讨论】:

        【解决方案3】:

        函数“F”采用函数“a”,该函数具有一组参数“x”。函数“F”返回一个与函数“a”具有完全相同的参数集的新函数。

        规范有点不清楚,但根据我对您的意思的理解,这是一个示例

        // Function 'F' takes function 'a' which has a set of parameters 'x'
        // and returns a new function which has exactly the same set of args as function 'a'
        function F<A extends Function>(a:A):A{
            return a;
        }
        
        var foo = F(function(a:number,b:number):void{});
        
        foo (1,3); // okay 
        foo(1); // ERROR
        

        更新

        您的用例示例:

        function f<A extends Function>(a:A):A {
            var newFunc = (...x:any[]) => {
                console.log('('+x.join(',')+') => ', a.apply(undefined, x));
                return null;
            }
            return <any>newFunc;
        }
        
        function a(j:string, k:number): boolean {
            return j === String(k);
        }
        
        var b = f(a);
        
        b("1", 1);
        b("a", 2);
        b('123','123'); // ERROR
        

        注意:我们已经捕获了参数返回类型。我看不到捕获只是参数的方法。

        【讨论】:

        • 我正在尝试为一个高阶函数编写类型规范,该函数返回一个函数,该函数的参数类型与传递给高阶函数的函数完全相同。高阶函数并不关心实际的 args 是什么,它只需要指定它返回的函数具有相同的 args。
        • @MarkGibson type specification for a higher-order function => type specification == F , higher-order function == F 对吧?
        • @MarkGibson 这就是你要问的:github.com/Microsoft/TypeScript/issues/2284 ?
        • 不,高阶函数返回的函数可能与传入的返回类型不同,唯一的共同点是它们的参数。我将在我的问题中添加一个示例。
        • 啊,谢谢@basarat,我知道你现在从哪里来,但正如你所说,它捕获的只是参数,这让我很头疼。
        猜你喜欢
        • 2023-02-21
        • 1970-01-01
        • 2020-06-17
        • 2018-11-19
        • 2019-06-28
        • 2019-07-25
        • 2018-02-21
        • 2021-07-28
        相关资源
        最近更新 更多