简体   繁体   中英

TypeScript: Can I provide a type alias for an overloaded function signature?

Is it possible to create a type alias for an overloaded function signature?

For example, I have a function like:

function whenChanged(scope: ng.IScope, fn: ()=>void): ()=>void;
function whenChanged(fn: ()=>void, truthy:any): ()=>void;
function whenChanged(a,b): ()=>void {
    //...
}

I would like to create type alias to that overloaded signature to save repetition, and make use of in other places where I need to describe the type of this function.

I tried:

type WC1 = (scope: ng.IScope, fn: ()=>void) => ()=>void;
type WC2 = (fn: ()=>void, truthy:any) => ()=>void;
type WhenChanged = WC1 | WC2;

const whenChanged: WhenChanged = (a,b) => {
    //...
};

But trying to use this function, I get an error along the lines of "Cannot invoke an expression whose type lacks a call signature".

I cannot offhand see anything in the docs about type aliasing function overloads.

Since you already have the function whenChanged :

function whenChanged(scope: ng.IScope, fn: ()=>void): ()=>void;
function whenChanged(fn: ()=>void, truthy:any): ()=>void;
function whenChanged(a,b): ()=>void {
    //...
}

The simplest way to get a type alias for its type is to use a typeof type query :

type WhenChanged = typeof whenChanged;

The next, most straightforward way to create the type alias is to use an overloaded function signature (which is what you were looking for):

type WhenChanged = {
  (scope: ng.IScope, fn: () => void): () => void;
  (fn: () => void, truthy: any): () => void;
}

(which is what you will see in quickinfo if you hover over the typeof definition in your editor.) Note that you don't need to use an interface if you don't want to.


The next thing you could do is similar to what you were doing, but the problem is that an overload is an intersection , not a union . It is both signatures:

type WC1 = (scope: ng.IScope, fn: ()=>void) => ()=>void;
type WC2 = (fn: ()=>void, truthy:any) => ()=>void;
type WhenChanged = WC1 & WC2; // and, not or

Be careful that intersections of function signatures are not commutative , specifically because they represent overloads. That means that the following type is not technically the same:

type NotWhenChanged = WC2 & WC1; // different type

since the overload resolution would take place in a different order.

Note that you can't call a function of type WC1 | WC2 WC1 | WC2 since the call signatures are different the compiler can't tell if the function is WC1 or WC2 .


Okay, there you go. Hope it helps; good luck!

After some playing, it turns out that the answer is (at least in part) to use an interface. This seems to work:

interface WhenChanged {
    (scope: ng.IScope, fn: ()=>void): ()=>void
    (fn: ()=>void, truthy:any): ()=>void
}

const whenChanged: WhenChanged = (a,b) => {
    //...
};

"Cannot invoke an expression whose type lacks a call signature" means a union of types isn't callable, you have to choose one type or the other.

The problem is that in the example you gave you haven't defined types for a and b , so the compiler cannot tell which of WC1 and WC2 is actually being used. If you change the definition of whenChanged :

const whenChanged: WhenChanged = (a: ng.IScope, b: ()=>void) => {
  return b;
};

now the compiler knows it can restrict the type of whenChanged to WC1 so you can safely call it.

If instead of a const you had a variable and whenChanged could be either of WC1 or WC2 in ways the compiler cannot tell then you will have to use some kind of type assertion to restrict the type to one or the other before calling it:

(whenChanged as WC1)(some_scope, some_fn);

or:

(whenChanged as WC2)(some_fn, some_truthy);

What you cannot do is work out from whenChanged which type it contains in order to call it with the appropriate arguments. You'll have to get that information from somewhere else.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM