type-fest
type-fest copied to clipboard
A type util to produce an alternation of function overloads.
Example situation:
type F = {
(thing: string): void;
(things: string[]): void;
}
type Z = Parameters<F>[0]
Type Z here resolves to string[]
, not string | string[]
, because overloads just get the last declared one matched.
I am curious to know if this is possible to implement. As far as I can tell this also affects the standard function overload approach, e.g.:
function fun(thing: string): void;
function fun(stuff: string[]): void;
// function fun(thing: string | string[]); // uncomment me to allow type Y to match `string`.
function fun(thing: string | string[]) { console.log(Array.isArray(thing) ? thing.join('; ') : thing); }
type F = {
(thing: string): void;
(things: string[]): void;
}
type Z = Parameters<F>[0]
type Y = Parameters<typeof fun>[0];
let zz:Z = 'abc';
let yy:Y = 'abc';
To be clear, what I'm asking for is a util e.g. OverloadParameters
which in this case would produce for type OverloadParameters<F>
a value of [string] | [string[]]
. ~~I'm actually currently unsure of how to unwrap the parameter-array situation there but I'm sure it's manageable.~~ As expected, the type ([string] | [string[]])[0]
is string | string[]
.
GPT4 plus testing shows to me that It appears possible to do this via
type OverloadParams<T> = T extends {
(...args: infer A): any;
(...args: infer B): any;
} ? A | B : never;
A general solution working for N overloads seems elusive. In practical terms we only ever need this for up to say 5 overloads. So unrolling it seems practical. A recursive implementation would be nice, but may lead to longer compilation time.
This definitely seems to work for me:
type OverloadParams<T> = T extends {
(...args: infer A);
(...args: infer B);
(...args: infer C);
(...args: infer D);
(...args: infer E);
} ? A | B | C | D | E : T extends {
(...args: infer A);
(...args: infer B);
(...args: infer C);
(...args: infer D);
} ? A | B | C | D : T extends {
(...args: infer A);
(...args: infer B);
(...args: infer C);
} ? A | B | C : T extends {
(...args: infer A);
(...args: infer B);
} ? A | B : never;