3

This question is about method overloading in TypeScript. I can define the interface Api without any problems. However if I implement it I get the error blow. I check which signature is called by checking the type of arg and return the right type accordingly. However the code does not compile. Unfortunately I can't find anything about overloading in the official TS docs.

interface Api {
  test(arg: string): string;
  test(arg: number): string[];
}

const api: Api = {
  test(arg: string | number) {
    if (typeof arg === "string") {
      return "string";
    }
    return ["string", "string"];
  },
};
Type '(arg: string | number) => string[] | "string"' is not assignable to type '{ (arg: string): string; (arg: number): string[]; }'.
  Type 'string[] | "string"' is not assignable to type 'string'.
    Type 'string[]' is not assignable to type 'string'.ts(2322)
7
  • 1
    I don't think you can do what you're trying to do in TS tbh. You're doing it "right" but it doesn't work when declaring an instance of an interface... you could do this when declaring a class that implements the API interface, but not a const Commented Jun 24, 2022 at 2:06
  • Is there a technical reason for this or does the language just not support it? Commented Jun 24, 2022 at 2:21
  • well, the issue that I can see is that for the function declaration to match the interface, you need to decorate the implementation with all of the overload signatures (as in, if you were doing this in a class implementation). But you can't do that in an instance / const declaration, so TS says it doesn't match the interface, and I don't see a way to make it match, so this seems like a limitation of the language, given how it matches overload interfaces with their implementation. Commented Jun 24, 2022 at 2:33
  • Sounds reasonable, thank you for the explantation. Commented Jun 24, 2022 at 2:40
  • Actually might be worth opening an issue about this on the repo. I think if the implementing class or object covers all the overloaded signatures, that should be fine. Commented Jul 24, 2022 at 14:17

2 Answers 2

3

Unfortunately as pointed out in the comments it is not possible to overload functions when declaring them in objects. What you can do - if you're willing to sacrifice on some convenience - is to declare your test function outside the object along with all the overload signatures and then assign it to that object.

function test(arg: string): string;
function test(arg: number): string[];
function test(arg: number|string) : string|string[] {
    if (typeof arg === "string") {
        return "string";
    }
    return ["string", "string"];
}

After the test function has been declared you can create a new object that implements the Api interface and assign your function to it.

interface Api {
    test(arg: string): string;
    test(arg: number): string[];
}

const api: Api = { test }

This is not entirely an amazing way to achieve what you want as any changes to the interface or the function declaration would also mean you'll have to change the other one as well.

There are some other answers from people way smarter than me under this question.
I know it's late, but better late than ever.

Sign up to request clarification or add additional context in comments.

Comments

0

If you're using classes, this works:

interface Api {
  test(arg: string): string;
  test(arg: number): string[];
}

class WhateverApi implements Api {
  test(arg: string): string;
  test(arg: number): string[];
  test(arg: string | number): string | string[] {
    throw new Error("Method not implemented.");
  }
}

It was mentioned in the comments, that this means you will need to add (and potentially expose) an extra class. Technically, you could use an anonymous class to fix that:

const api = new class implements Api {
  test(arg: string): string;
  test(arg: number): string[];
  test(arg: string | number): string | string[] {
    throw new Error("Method not implemented.");
  }
};

2 Comments

That's also a good way of working this out, but one small catch would be that you'd either have to expose that class to the outside world already initialized or use static methods.
Sure, you would then need to call const api = new WhateverApi(). You could technically use an anonymous class, I'll edit my answer and add that, lol.

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.