question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Unexpected overload signature rejection since TypeScript 4.8.x

See original GitHub issue

Bug Report

🔎 Search Terms

  • This overload signature is not compatible with its implementation signature.
  • HTMLElementTagNameMap

🕗 Version & Regression Information

  • This changed between versions 4.7.4 and 4.8.2

⏯ Playground Link

Playground link with relevant code

💻 Code

export function childByTag<K extends Uppercase<keyof HTMLElementTagNameMap>>(
  element: Element,
  tagName: K,
): HTMLElementTagNameMap[Lowercase<K>] | null; // this overload is rejected
export function childByTag(element: Element, tagName: string): Element | null;
export function childByTag(element: Element, tagName: string): Element | null {
  for (let i = 0; i < element.childElementCount; i++) {
    if (element.children[i].nodeName === tagName) {
      return element.children[i];
    }
  }

  return null;
}

const anchor = childByTag(document.documentElement, 'A');
if (anchor) {
  console.log(anchor.href); // Would be rejected without the first overload.
};

🙁 Actual behavior

TypeScript rejected the first function overload, because “This overload signature is not compatible with its implementation signature”.

🙂 Expected behavior

I didn’t expect the code to be rejected, because:

  • To my understanding all the values in HTMLElementTagNameMap are subtypes of Element, thus being a covariant return type.
  • All the keys in HTMLElementTagNameMap are strings, thus matching a strict subset of all possible strings that may be passed to tagName. The remaining strings are matched by the second overload.

If the rejection is valid and my analysis is wrong, then I would have expected the error message to be more helpful in pointing out the problem.

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:2
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
weswighamcommented, Sep 14, 2022

You can’t rewrite it to be more correct, no, since I’d say it’s already correct enough. But you can hack the typesystem to get the desired result with a workaround like

type Cast<A, B> = A extends B ? A : A & B;

export function childByTag<K extends Uppercase<keyof HTMLElementTagNameMap>>(
  element: Element,
  tagName: K,
): HTMLElementTagNameMap[Cast<Lowercase<K>, keyof HTMLElementTagNameMap>] | null;
0reactions
mosesoakcommented, Sep 30, 2022

I have a similar problem in this version. Here’s an example signature with overloads as I originally wrote it. Note that there is a 2nd parameter, whenKnown which would either be not passed or be passed as true only.

export function useMedia(mediaQuery: string, whenKnown: true): undefined | boolean;
export function useMedia(mediaQuery: string): boolean;
export function useMedia(mediaQuery: string, whenKnown?: true) { ... }
export function useIsMobile(): boolean;
export function useIsMobile(whenKnown: true): undefined | boolean;
export function useIsMobile(whenKnown?: true) {
  return useMedia(mobleMqStr, whenKnown);
/*------------------------------^^^ 
Error: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.*/
}

It seems that because the inputs are variable / overloaded, it’s finding errors with one or more of the callee overloads. Of course this works but hopefully it’s not the only way to work around this error:

  if (whenKnown) {
    return useMedia(mobileMqStr, true);
  }
  return useMedia(mobileMqStr);

So in the latest version, what’s the correct way to do this?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Documentation - More on Functions - TypeScript
This overload signature is not compatible with its implementation signature. function fn ( x : number): boolean; This overload signature is not compatible...
Read more >
Overload Signatures and the Implementation ... - Stack Overflow
Turns out in Typescript, the declaration of overload is not by having two methods with the same name and different signature with each...
Read more >
TypeScript errors and how to fix them
error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'Promise ' has no compatible call signatures. Broken Code ❌....
Read more >
Firebase JavaScript SDK Release Notes - Google
0 upgraded TypeScript only in the root. Fixed a bug that caused Firebase SDKs to throw an error in Firefox browsers when third-party...
Read more >
@angular/animations | Yarn - Package Manager
angular-logo. Angular is a development platform for building mobile and desktop web applications using Typescript/JavaScript and other languages.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found