How can I infer Typescript argument types for a single implementation with overloaded signatures?

Viewed 29

I have two signatures for my call method, and a single implementation:

call<T extends CallChannel, TArgs extends CallParameters[T]>(channel: T, ...args: TArgs): ReturnType<CallListener<T>>;
call<T extends SharedChannel, TArgs extends SharedParameters[T]>(channel: T, ...args: TArgs): ReturnType<SharedListener<T>>;
call(channel, ...args) { ... }

However Typescript won't typecheck because the arguments to call are implicitly typed any. Obviously they shouldn't be, they must be either CallChannel or SharedChannel type for the first argument, and similarly the second argument is one of two specific types.

But I can't define the implementation like this:

call<T extends CallChannel | SharedChannel, TArgs extends CallParameters[T] | SharedParameters[T]> (channel: T, ...args: TArgs) {

because the type CallParameters[T] can't resolve when T is a SharedChannel (and vice versa).

I have already provided all of the type information necessary to define call, how can I make typescript happy with the signature of the implementation?

1 Answers

You should be able to do this with conditional types.

Define a conditional type for args and for the return.

    type Args<T extends CallChannel | SharedChannel> = 
        T extends CallChannel 
        ? CallParameters[T] 
        : T extends SharedChannel 
        ? SharedParameters[T] 
        : never

Then update the function to use it:

 TArgs extends Args<T>

Return can use the same principle.

Related