1
votes

I have the following code:

export interface IStartCreate1 {
  (desc?: string, opts?: IDescribeOpts, arr?: Array<string | IDescribeOpts | TCreateHook>, fn?: TCreateHook): void;
  tooLate?: boolean;
}

export interface IStartCreate2 {
  (opts?: IDescribeOpts, arr?: Array<string | IDescribeOpts | TCreateHook>, fn?: TCreateHook): void;
  tooLate?: boolean;
}

export interface IStartCreate3 {
  (arr?: Array<string | IDescribeOpts | TCreateHook>, fn?: TCreateHook): void;
  tooLate?: boolean;
}

export interface IStartCreate4 {
  (fn: TCreateHook): void;
  tooLate?: boolean;
}

export type IStartCreate = IStartCreate1 | IStartCreate2 | IStartCreate3 | IStartCreate4;

Then I have an object like so:

const v = {
  create: function(){} as IStartCreate
} 

v.create([]);

I get this error message:

Cannot invoke an expression whose type lacks a call signature.

I would think an empty array would match IStartCreate3

I have looked at other SO questions with a similar error message and I cannot figure this one out!

2
This seems to describe the problem: github.com/Microsoft/TypeScript/issues/7294Alexander Mills

2 Answers

2
votes

As of 2017, looks like union types cannot have a call signature:

https://github.com/Microsoft/TypeScript/issues/7294

Seems pretty lame.

So I had to do away with the union type and do it a different way, looks like this:

export type TArray = Array<string | IDescribeOpts | TCreateHook>;

export interface IStartCreate {
  (desc: string | IDescribeOpts | TCreateHook | TArray,
   opts?: IDescribeOpts | TCreateHook | TArray,
   arr?: TArray | TCreateHook,
   ): void;
  tooLate?: boolean;
}
1
votes

It would be helpful to understand what you are trying to achieve. Generally speaking, this seems like an extremely complicated type declaration that is likely to cause problems.

Your declaration of IStartCreate states that it is either a IStartCreate1 or IStartCreate2 or 3, but we don't know which. You will therefore only be able to access members/signatures on it that exist in all of IStartCreate1, IStartCreate2 and IStartCreate3 (in other words, only tooLate). The method signature you're looking for is not in all of them.

If you are sure that v.create in fact implements IStartCreate3, you can do (v.create as IStartCreate3)([]), but it will cause runtime errors if it turns out to be of a different type.

Generally speaking, function overloading doesn't work particularly well in TypeScript, and I would normally try to avoid doing it.