2

I have come across an error related to function overloading in typescript and have written this minimal example to reproduce the error.

interface XComplex {
  value: X
}
type X = string | XComplex

interface YComplex {
  value: Y
}
type Y = string | YComplex

function transform(v: string): string
function transform(v: XComplex): YComplex
function transform(x: X): Y {
  if (typeof x === "string") {
    return x
  }

  const xValue: X = x.value

  return {
    value: transform(xValue), // <---- **ERROR**
  }
}

// **ERROR**
// [ts]
// Argument of type 'X' is not assignable to parameter of type 'XComplex'.
//   Type 'string' is not assignable to type 'XComplex'.

The error does not occur if I remove the overloaded signatures but then the following use cases would not work.

// WITH OVERLOADING
const yString1: string = transform('hello')
const yComplex1: YComplex = transform({ value: 'xValue'})
// WITHOUT OVERLOADING
const yString2: Y = transform('hello')
const yComplex2: Y = transform({ value: 'xValue'})

In the error it seems that the "Argument of type 'X'" is xValue which is "not assignable to parameter of type 'XComplex'" which would be the parameter of transform. But transform takes a parameter of type X so I'm not sure what I am doing wrong.


EDIT 1:

It seems that if I add an extra overloaded signature it works as intended.

function transform(v: string): string
function transform(v: XComplex): YComplex
function transform(v: X): Y <--- EXTRA OVERLOADED SIGNATURE
function transform(x: X): Y { ... }

Now I'm just not sure why this is necessary or how to have understood this from the error message.


EDIT 2:

After thinking about this more, I guess since xValue is of type X, typescript could not possibly know whether it is a string or an XComplex and so none of

function transform(v: string): string
function transform(v: XComplex): YComplex

would match. I assume then that typescript is just going through the signatures one by one until it reaches the last one and realises none match so it just outputs "Argument of type 'X' is not assignable to parameter of type 'XComplex'." referring to the last signature. An error stating that no overloaded signatures match seems like it would be more helpful.

This explains why the kind of 'catch all' case

function transform(v: X): Y <--- EXTRA OVERLOADED SIGNATURE

is necessary.

1 Answer 1

4

The implementation signature of an overload is not callable, so if you want to have a callable version of your overload that accepts X you need to add a non-implementation signature for it.

When you use overloads, the implementation signature cannot be called directly, so all calls must be compatible with one of the overloads - Pro TypeScript p39

function transform(v: string): string;     // Overload
function transform(v: XComplex): YComplex; // Overload
function transform(x: X): Y {              // Implementation Signature

So although it looks like duplication, the correct fix is an additional overload:

function transform(v: string): string;
function transform(v: XComplex): YComplex;
function transform(x: X): Y;
function transform(x: X): Y {
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.