Skip to content

An overloaded function cannot call identical overloaded function with its parameters #53318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mckravchyk opened this issue Mar 17, 2023 · 2 comments
Labels
Duplicate An existing issue was already created

Comments

@mckravchyk
Copy link

Bug Report

🔎 Search Terms

no overload matches call implementation succeed

🕗 Version & Regression Information

At least in 4.9 and it does occur in the current nightly

This is the behavior in every version I tried, and I reviewed the FAQ for entries.

⏯ Playground Link

Playground link with relevant code

💻 Code

function innerGet(): Record<string, string>;
function innerGet(id: string): string | null;
function innerGet(ids: string[]): Record<string, string | null>;
function innerGet(idOrIds?: string | string[]): Record<string, string | null> {

}

function outerGet(): Record<string, string>;
function outerGet(id: string): string | null;
function outerGet(ids: string[]): Record<string, string | null>;
function outerGet(idOrIds?: string | string[]): Record<string, string | null> {
  return innerGet(idOrIds);
  // No overload matches this call.
  // The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
}

🙁 Actual behavior

Trying to call a function with the same overloaded parameters will result in a "no overload matches this call" error.

🙂 Expected behavior

There should be no error. It's pretty clear that the types are compatible.

@MartinJohns
Copy link
Contributor

Duplicate of #39885 / #1805.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Mar 17, 2023
@mckravchyk
Copy link
Author

mckravchyk commented Mar 17, 2023

Ok, thanks. I never came across this in 3 years of using TS and I have been using plenty overloads. It's good to know that, better late then never but I wished the docs mentioned it. This is an issue that will not come up immediately, so there may be plenty of code using overloads that will need to be refactored upon realization that there is a major bug that has been marked as wontfix. I only found: "Always prefer parameters with union types instead of overloads when possible" but I don't think it sufficiently warns against it. I would interpret it as "use union types if you don't need to narrow the return type based on the input arg".

And when I try to implement the same with a conditional type in the return, I find myself between the rock and a hard place. I run into a bug I encountered earlier #52051 . As a matter of fact, my workaround to that bug was using overloads :)

Edit: It seems to be possible to override it with casting after all.

function get<Arg extends string | string[] | undefined>(
  idOrIds: Arg
) : Arg extends string ? string | null
  : Arg extends string[] ? Record<string, string | null>
  : Record<string, string> {
  if (typeof idOrIds === 'string') {
    return ''; // Not assignable
  }
    
}

function get2<Arg extends string | string[] | undefined>(
  idOrIds: Arg
) : Arg extends string ? string | null
  : Arg extends string[] ? Record<string, string | null>
  : Record<string, string> {
  if (typeof idOrIds === 'string') {
    return '' as ReturnType<typeof get2<Arg>>; // Ok
  }
    
}

Playground

Edit2: Hmm, actually I cannot get the above function to work properly when the argument is not supplied. I will investigate it further and update this if anything applies to the current bug.

Edit3: It's possible to use a combo of overload to deal with the missing parameter and conditional types to deal with the union bug.

function get3(): Record<string, string>;
function get3<Arg extends string | string[] | undefined>(
  idOrIds?: Arg
) : Arg extends string ? string | null
  : Arg extends string[] ? Record<string, string | null>
  : Arg extends undefined ? Record<string, string> : never;
function get3(idsOrId?: string | string[]): string | null | Record<string, string | null> | Record<string, string>
   {
  if (typeof idsOrId === 'string') {
    return '';
  }
    
}


let a = get3('');
let b = get3([]);
let c = get3(undefined);
let d = get3();

function get4(): Record<string, string>;
function get4<Arg extends string | string[] | undefined>(
  idOrIds?: Arg
) : Arg extends string ? string | null
  : Arg extends string[] ? Record<string, string | null>
  : Arg extends undefined ? Record<string, string> : never;
function get4(idsOrId?: string | string[]): string | null | Record<string, string | null> | Record<string, string> {
  return get3(idsOrId);
}

Playground

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants