9

Background

So I have a problem getting specific Parameters from a function which is overloaded. For example:

// someLib.d.ts
type Component<T> = {};
type A = {};
type B = {};
type C = {};
type Opts = {};
type ModernOpts = {};

export declare function mount(component: A, options: Opts): Component<A>;
export declare function mount(component: B, options: Opts): Component<B>;
export declare function mount(component: C, options: ModernOpts): Component<C>;

The problem is, somehow if I do this on another file:

import { mount } from 'someLib';

type specificMountParams = Parameters<typeof mount>;

The param I get is [C, ModernOpts], and it seems like there's no way to get the parameter of [A, Opts], or [B, Opts].

Question

Is there any way to retrieve specific parameter from overloaded functions? (So I can get [A, Opts] parameter)

Limitation and Info

Those types (A, B, Opts) are not exported by the library, and I need to create a function that needs such type to do something similar.

akasection
  • 93
  • 6
  • This is actually possible, and this question should probably be marked as a duplicate. The solution's here: https://stackoverflow.com/questions/52760509/typescript-returntype-of-overloaded-function – Kyle Jul 06 '23 at 11:05
  • @Kyle first, the solution above specifically works on ReturnType, and second, it merges the result as union. The thing I want is to retain/pick ONLY ONE of the overloaded functions. probably from your ref, we can twist it to return it as tuples instead of union and pick the [0-x] index, but it might still be inaccurate. – akasection Aug 17 '23 at 14:24

1 Answers1

3

From the docs:

When inferring from a type with multiple call signatures (such as the type of an overloaded function), inferences are made from the last signature (which, presumably, is the most permissive catch-all case). It is not possible to perform overload resolution based on a list of argument types.

COnsider this example:

function foo(a: number): number
function foo(a: string): string // last signature
function foo(a: number | string): number | string {
  return null as any
}

type Fn = typeof foo

// returns last overloaded signature
type Arguments = Parameters<Fn> // [a: string]

Parameters always returns last overdloaded signature of function.

Try to change the order:

function foo(a: string): string 
function foo(a: number): number// last signature
function foo(a: number | string): number | string {
  return null as any
}

type Fn = typeof foo

// returns last overloaded signature
type Arguments = Parameters<Fn> // [a: number]

There is no way to return a union of all parameters because it is unsound. See official explanation here

It's not really possible to make this in a way that's both useful and sound. Consider a function like

declare function fn(n1: number, n2: number): void;

declare function doCall<T extends (a1: any, a2: any) => void>(func: T,
a0: Parameters<T>[0], a1: Parameters<T>[1]): void; ```

If Parameters<T>[0] returns string | number, then doCall(fn, 0, "") would incorrectly succeed. If Parameters<T>[0]> returns string & number, then doCall(fn, 0, 0) would incorrectly fail (and be a big breaking change). Notably, with conditional types and unions, really the only kind of functions that can't be typed with a single overload are exactly the ones that have this failure mode.

The current behavior at least makes it so that some calls are correctly accepted.

You can find some workarounds in above github thread

  • 3
    I kinda get it if the union type isn't possible, but extracting/picking specific overload function (params & return types) while skipping union process is supposedly still _sound_... But yeah I understand that this is kind of limitation. It's just a bit sad I can't recreate a fully typed function from another function... – akasection Aug 19 '21 at 11:06