This is by design. It always returns last overloaded signature. See this issue/28789
However, there is another way.
Please keep in mind that overloading is just an intersection of function types.
We can declare a type which will hold all our overloading signatures:
type Signatures = {
1: (x: string) => number,
2: (x: string, y: number) => 42
3: (x: number) => string
}
Now, in order to create overloadings, we need to obtain a union of all object properties and intersect them.
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Values<T> = T[keyof T]
type Overloading = UnionToIntersection<Values<Signatures>>
It works as expected:
declare const foo: Overloading;
const a = foo(123); // string
const b = foo('123'); // number
const c = foo('123', 456); // 42
Now, it is very easy to obtain a union of all allowed parameters:
// [x: string] | [x: string, y: number] | [x: number]
type fooArgs = Parameters<Values<Signatures>>;
declare function bar(...args: fooArgs): string[]
const x = bar(123); // string[]
const y = bar('123'); // string[]
const z = bar('123', 456); // string[]
Whole code:
type Signatures = {
1: (x: string) => number,
2: (x: string, y: number) => 42
3: (x: number) => string
}
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Values<T> = T[keyof T]
type Overloading = UnionToIntersection<Values<Signatures>>
declare const foo: Overloading;
const a = foo(123); // string
const b = foo('123'); // number
const c = foo('123', 456); // 42
// [x: string] | [x: string, y: number] | [x: number]
type fooArgs = Parameters<Values<Signatures>>;
declare function bar(...args: fooArgs): string[]
const x = bar(123); // string[]
const y = bar('123'); // string[]
const z = bar('123', 456); // string[]
Playground