TypeScript: Union of function signatures cannot be invoked because it lacks a call signature

Reduced test case:

var foo: ((arg: string) => void) | ((arg: any) => void);
foo(null);
  1. With 1.5.3, this compiles fine. With 1.6-beta, this gives error TS2349: Cannot invoke an expression whose type lacks a call signature. null is assignable to both string and any so it should compile.
  2. Maybe related - getting quickinfo for foo on the second line does throw an error TypeError: Cannot read property 'flags' of undefined in playground even though it compiles fine with 1.5.3. I didn’t try quickinfo on 1.6
  3. If foo was ((arg: string) => void) | ((arg: number) => void); (number instead of any in the second signature) then both 1.5.3 and 1.6 give that error, even though again null is assignable to both.

For motivation, the original code is https://github.com/Arnavion/libjass/blob/6606413/src/utility/promise.ts#L363 (the error is on handler). It’s an implementation of the ES6 promise spec, specifically http://www.ecma-international.org/ecma-262/6.0/#sec-promisereactionjob - handler would be either the fulfilled callback or rejected callback and the argument passed to it would be either the resolution value or the rejection reason accordingly, hence its signature.

(I understand it would be more type-safe to have separate handlers for the two cases with one signature each from the union, but that complicates the implementation slightly.)

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 2
  • Comments: 15 (8 by maintainers)

Most upvoted comments

Hmmm. A scenario like following still throws this error in 1.8.7, which doesn’t seem like it should:

var arr:(Array<number>|Array<string>) = [5];
arr.map(String);

However if making it more permissive:

var arr:Array<number|string> = [5];
arr.map(String);

or restricted:

var arr:Array<number> = [5];
arr.map(String);

fixes the error.

It seems this is still an issue in TS 2.4.2

export type MyControllerHandler = (e:MyValueEvent)=>void;
export type MyPatchHandler = (e: MyPatchEvent)=>void;

export type MyHandler = MyPatchHandler | MyControllerHandler;


declare const onChange: MyHandler;
onChange({}as any);  // Error:(42, 17) TS2349:Cannot invoke an expression whose type lacks a call signature. Type 'MyHandler' has no compatible call signatures.

This fixes the error:

export type MyHandler = (e:MyValueEvent | MyPatchEvent)=>void;

I am running into this in 2.6.2, it seems odd to me that this doesn’t work. Is this working as intended?

Here’s pretty simple example:

class TestClass {
    public foo(x: string | number) {
    }
}
class TestClass2 {
    public foo(x: string) {
    }
}
const bar: (TestClass | TestClass2) = new TestClass();
bar.foo("baz"); // Cannot invoke an expression whose type lacks a call signature. 
                // Type '((x: string | number) => void) |
                //       ((x: string) => void)' has no compatible call signatures.

I am getting around it currently by casting to an interface with the “correct” union.

interface FooShim {
    foo(x: string | number): void;
}
(bar as FooShim).foo("baz");

@dkoprowski You want (bar.handler as A)(false)