Repository navigation
Union of function signatures cannot be invoked because it lacks a call signature #4612
Description
Activity
I guess it has to do with the fact that the union type doesn't contain call signatures unless the signature parameters match exactly in all constituents types 3.11.1. So does that mean the bug was in 1.5.3 to allow it to compile in the first place?
This a result from removing the aggressive subtype reduction that was in 1.5.* (see #3823). Now we are not reducing to subtypes, so the type still contain the two signatures. at time when the call resolution happen 3.11.1 takes effect, and no call signatures are found.
This is a breaking change from 1.5.3 (will add it to the docs).
Also related to #3823
- addedBreaking ChangeWould introduce errors in existing codeWould introduce errors in existing code
on Sep 3, 2015 Does not reducing to a subtype with one signature help prevent any errors?
And is there a way to make this work with 1.6 while still keeping the union type?
You could use call, apply or bind. I know it´s very ugly but It works.
var foo: ((arg: string) => void) | ((arg: number) => void) = function(a:string):void{alert(a)}; var foo2: ((arg: string) => void) | ((arg: number) => void) = function(a:number):void{alert(a)}; foo.call(this, "a"); // Be careful with "this". It´s a context. foo2.call(this, 1); // Be careful with "this". It´s a context.Yeah. Splitting into two handlers is cleaner, so I'll go with that.
Arnav Singh (@Arnavion), the aggressive subtype reduction would causes cause these errors to disappear, but it also caused loss of information from the union type. the fix was to keep the union information as long as possible. Subtype reduction now is only happening on the lever of operators like conditional and Or (see #3823). in this sense something like:
var foo1: ((arg: string) => void) var foo2: ((arg: any) => void); var foo = foo1 || foo2; foo(null); // works
Looks like we also get this error when both types are derived from the same base class and we try calling a method of the base class. Is that expected as well?
class Base { foo() { } } class Derived1 extends Base { } class Derived2 extends Base { } var spam : Derived1 | Derived2 = ... spam.foo(); <- error lacking call signatureDouglas Armstrong (@drarmstr), this should not be an error. i think there is something else going on. also this is an old issue. please file a new one with more information about your code and the version of the compiler you are using.
RyanCavanaugh commented
on Feb 12, 2016 MemberMore actionsDouglas Armstrong (@drarmstr) that code doesn't issue an error when I compile with the latest TypeScript
Yeah, the scenario was from a much more complicated code base. If I can get a simpler test case to reproduce then I'll submit a new issue. I switched to using an interface for the common methods, though, and it's been working for me.
On Feb 12, 2016, at 12:23, Ryan Cavanaugh notifications@github.com wrote:
Douglas Armstrong (@drarmstr) that code doesn't issue an error when I compile with the latest TypeScript
—
Reply to this email directly or view it on GitHub.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.
Reacted by Sebastian Lasse, Dimitri, Julien Valéry and Hongbo MiaoIt 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;
Reacted by Amir Arad, Conor Rice and Jiayi HuI 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");
Reacted by Tianyun (Maria) and Alexandre GalaysI have this issue in 2.8.1, those workaround doesn't apply for me because I would like to overload a function.
Example of my overload:
type A = (b: boolean) => void; type B = (a: string, b?: boolean) => void; interface Props { handler: A | B } (bar as Props).handler(false); //TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'A | B' has no compatible call signatures.
RyanCavanaugh commented
on May 9, 2018 MemberMore actionsDaniel Koprowski (@dkoprowski) You want
(bar.handler as A)(false)Reacted by Daniel Koprowski- locked and limited conversation to collaborators
on Jul 31, 2018
Reduced test case:
error TS2349: Cannot invoke an expression whose type lacks a call signature.null is assignable to both string and any so it should compile.TypeError: Cannot read property 'flags' of undefinedin playground even though it compiles fine with 1.5.3. I didn't try quickinfo on 1.6((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.)