4

Using Typescript 1.4, say I have a higher-order function with the signature:

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

Function 'F' takes function 'a' which has a set of parameters 'x'. Function 'F' returns a new function which has exactly the same set of args as function 'a'.

I've been trying to find a way to express this in Typescript, but i'm coming up short. For example:

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

the compiler just complains with: error TS2370: A rest parameter must be of an array type.

Although that doesn't feel right anyway, even if it did compile. I guess i'd really need something like:

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

Anyone know if this kind of thing is even possible with Typescript currently (1.4 at time of writing)? or any suggestions?


Example:

(NOTE: This is not my actual use-case, i'm only using logging here as a simple example - please don't focus on that aspect)

// 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);

console output:

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

So this works, but the derived function 'b' has the implied signature of:

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

ideally i'd like it to have the same arguments as function 'a' ie:

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

I know I could explicitly define that, but it's very verbose and not at all ideal, kind of defeats the point of having the strong typing in the first place:

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

3 Answers 3

1

To wrap a function with another that has different return type, one hacky trick is possible with overloading:

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

Be careful, it will work only for functions with limited number of arguments.

Sign up to request clarification or add additional context in comments.

Comments

0

Function 'F' takes function 'a' which has a set of parameters 'x'. Function 'F' returns a new function which has exactly the same set of args as function 'a'.

The specification is a bit unclear, but based on my understanding of what you meant, Here is a sample

// 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

UPDATE

An example for your use case:

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

Note: We've capture both the arguments and the return type. I don't see a way to capture just the arguments.

5 Comments

I'm trying to write the type specification for a higher-order function that returns a function which has the exact same argument types as the function passed into the higher-order function. The higher-order function doesn't care what the actual args are, it just needs to specify that the function it returns has the same args.
@MarkGibson type specification for a higher-order function => type specification == F , higher-order function == F right?
@MarkGibson is this what you are asking : github.com/Microsoft/TypeScript/issues/2284 ?
No, the function returned by the higher-order function may have a different return type to that passed in, the only commonality is their arguments. I'll add an example to my question.
Ah, thanks @basarat, i see where you are coming from now, but as you say its the capturing of just the arguments that's causing my headache.
0

It's currently not possible to express this in the TypeScript type system. It's a long standing issue which you can follow on https://github.com/Microsoft/TypeScript/issues/212 and https://github.com/Microsoft/TypeScript/issues/5453

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.