Home > Software engineering >  Using call on a union of multiple function types differing by return type
Using call on a union of multiple function types differing by return type

Time:05-02

My function has a callback typed as a union of multiple function types, each having the same set of formal arguments, but different return types. This code:

type FA = () => string;
type FB = () => number;

function f( callback: FA | FB ) {
    callback.call( {} );
}

... produces TS2684 error:

(TS) The 'this' context of type 'FA | FB' is not assignable to method's 'this' of type '(this: {}) => string'.
    Type 'FB' is not assignable to type '(this: {}) => string'.
        Type 'number' is not assignable to type 'string'.

However, this:

type FixCallbackUnion<T extends ( this: ThisType<T>, ...a: any ) => any> =
    ( this: ThisType<T>, ...a: Parameters<T> ) => ReturnType<T>;

type FA = () => string;
type FB = () => number;

function f( callback: FA | FB ) {
    ( <FixCallbackUnion<typeof callback>>callback ).call( {} );
}

... compiles normally while seemingly keeping all the involved types exactly the same.

How can I fix the first example to make it work, or do I have to use a workaround from the second one?

CodePudding user response:

..should look something like this: Sorry.. I tested this in a class, to make this example work I had to call the functions directly. Anyway.. using .call(this?) may make sense in your code anyway. To get two different types as return value (I guess thats the question about) you have to specify them AND of course return the return-value of the inner function as well.

Let me know if I missed something.

type FA = () => string;
type FB = () => number;

function f( callback: FA | FB ): string | number {
    return callback();
}

let A: FA = function() { return "string"; }; 
let x = f(A);
console.log(x);

let B: FB = function() { return 3; };
let y = f(B);
console.log(y);

CodePudding user response:

As @jcalz points in the comment, this is a shortcoming of TypeScript as of version 4.6.2 (#33815).

The workaround is to use call on a value that's no longer a union of function types, by either specifying the type explicitly on your own or using the type system to compute the required type for you (see the question for an example on how to do that).

  • Related