TypeScript
TypeScript copied to clipboard
Overloaded function args not inferred correctly
Bug Report
🔎 Search Terms
overload functions
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about overloading and different function arguments behavior
⏯ Playground Link
Playground link with relevant code
💻 Code
function a1(arg1: unknown): 1;
function a1(arg1: unknown, arg2: unknown): 2;
function a1(...args: ([arg1: unknown] | [arg1: unknown, arg2: unknown])): 1 | 2 {
return args.length;
}
const b1: typeof a1 = function f1(...args) {
return a1(...args);
}
even simpler is using an interface
interface a1 {
(arg1: unknown): 1;
(arg1: unknown, arg2: unknown): 2;
}
// errors
const b1: a1 = function f1(...args) {
return args.length;
}
🙁 Actual behavior
Error: Target signature provides too few arguments. Expected 2 or more, but got one., given the single signature of b1 above is variadic, and in fact matches the implementation signature of a1, one would assume the two functions are equivalent (from a signature perspective)
🙂 Expected behavior
The type of args in b1 should be inferred as the union of all overloads of a1 since it is the only way the variadic args would satisfy the assigned typeof a1 requirement.
The logic that's going wrong here is that we fetch the contextual type to give to ...args based on the LHS argument lists. There we get [unknown], and [unknown unknown], and reduce that to [unknown unknown] since in general a longer list of parameter contextual types can only help you. Here, it hurts you since it makes the args tuple type too long for one of the signatures.
function a1(arg1: unknown): 1;
function a1(arg1: unknown, arg2: unknown): 2;
function a1(...args: ([arg1: unknown] | [arg1: unknown, arg2: unknown])): 1 | 2 {
return args.length;
}
const b1: typeof a1 = function f1(...args) {
return a1(...args);
}
const b2: typeof a1 = function f1(arg1, arg2?) {
return 1;
}
I believe we just need to special case rest args to handle their contextual type a little bit differently
Happens for me aswell
Weirdly enough, the reported above is from the playground but locally the same code with Node 18 and TS 5.1 will report
test.ts:7:7 - error TS2322: Type '(...args: any[]) => number' is not assignable to type 'a1'.
Type 'number' is not assignable to type '1'.
7 const b1: a1 = function f1(...args) {
~~
In the mean time, is there a workaround to get this kind of overloading functionality without triggering this bug?
Minimal reproduction typescriptlang.org/play?#
interface Options {}
interface Props {
action: {
(): void
(newOptions: Partial<Options>): Options
}
}
declare const x: Props
const a = x.action({}) // correctly: Options
const b = x.action() // correctly: void
x.action = (newOptions) => { // Error: Target signature provides too few arguments. Expected 1 or more, but got 0.(2322)
if (newOptions) {
return newOptions
}
return
}
Since the type inference works properly outside of the implementation, I guess I will resort to this in the meantime.
// @ts-expect-error Target signature provides too few arguments. Expected 1 or more, but got 0.
x.action = (newOptions) => {
...
Hi,
Workaround for this is to have always the same number of arguments, but typing unneeded one as optional never.
interface a1 {
(arg1: unknown, arg2?: never): 1;
(arg1: unknown, arg2: unknown): 2;
}
Note: In the example, we still have an issue: Type '2' is not assignable to type '1'. This workaround only works if your function always return the same type.
May I know how b is inferred as unknown instead of boolean?
function xx(a: string): void;
function xx(a: string, b: boolean, c?: string): void;
function xx(a: string, b = true, c?: string): void {
console.log(a, b, c)
}
const xxx: typeof xx = function xxx(a: string, b = true, c?: string): void { // b is unknown here
xx(a, b, c); // Argument of type 'unknown' is not assignable to parameter of type 'boolean'.
};
And if I turn off noImplicitAny, b can be inferred as boolean.