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.