I was extremely confused by this also. Coming from a Kotlin/Java Android background, I have to say the overloading machines of Typescript take a while to grasp.
First we need to address how to declare overload in Typescript
Concern 1: Incorrect way of declaring overloads [not the cause of issue for the question]
My initial idea about handling overloading is to write the following code:
function makeDate(timestamp: number): void { /* code */ }
function makeDate(m: number, d: number, y: number): void { /* code */ }
I was immediately hit by Duplicate function implementation.ts(2393) lint error. I was confused at first, since this is how you declare overload in Java and Kotlin. However, this is actually not how you declare overload in Typescript and I believe this is the main cause of issues.
Answer to concern 1:
Turns out in Typescript, the declaration of overload is not by having two methods with the same name and different signature with each of their own implementations. Instead, it is by defining methods with the same name and different signatures without body first, and lastly provide a method with implementation (method body), that has the ability to handle all the previously declared bodyless methods.
Concern 2: Incorrect amount of argument for the overload requirement [cause of issue for the question]
Now that I have figured out the correct way to create overloaded methods in Typescript, I went straight ahead to write the following code:
[code 2.1]
function fn(x: string): void; // <-- Define one way to call method
function fn(x: string, y: string, z: string): void; // <-- Define another way to call method with more param (overloaded way)
function fn(x: string, y?: string, z?: string): void { // <--- provide a method with a body that can handle both previous two declarations
if (y === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x} :: ${y} :: ${z}`)
}
}
fn("x")
fn("xxx", "yyy", "zzz")
Outputs:
$ branch 1
$ branch 2 -> xxx :: yyy :: zzz
In the code snippet above, we declared 1 method with 2 different overloads.
- overload 1 ->
function fn(x: string): void;
- overload 2 ->
function fn(x: string, y: string, z: string): void;
And they are both handled by "concrete" method:
function fn(x: string, y?: string, z?: string): void {
if (y === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x} :: ${y} :: ${z}`)
}
}
Since this method has a body, that is why I call it concrete method. Also notice that this method handles the case with @params: x, and with @params: y and z being optional, by doing this it covers the method calls {overload1 and overload2}. And this is a valid overload.
Following the same mindset I then went ahead and wrote the following code:
[code 2.2]
function fn2(x: string): void;
function fn2(x: string, y: string): void;
function fn2(x: string, y?: string): void {
if (y === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x} :: ${y}`)
}
}
fn2("x")
fn2("x", "y")
Outputs:
$ branch 1
$ branch 2 -> x :: y
The code also worked, which just like what I suspected. So I wrote some more code:
[code 2.3]
function fn3(): void;
function fn3(x?: string): void {
if (x === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x}`)
}
}
fn3()
fn3("x")
But this time the code did not work and tsc is complaining:
Playground.ts:219:5 - error TS2554: Expected 0 arguments, but got 1.
219 fn3("x")
~~~
Found 1 error.
Now this is the time we should spend some time understanding the quote from the documentation:
The signature of the implementation is not visible from the outside. When writing an overloaded function, you should always have two or more signatures above the implementation of the function.
My understanding below is not correct. I made the mistake here mixing args and signature. So text below is not correct. Leaving it here as a bad example. See @David's answer below as he provide a good explanation as why this is incorrect. For example: the code snippet attached below is also valid overload with 0 ~ 1 args. This can be compared with[code 2.3] which yield syntax error.
function fn3(): void;
function fn3(x: string): void;
function fn3(x?: string): void {
if (x === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x}`)
}
}
> calling:
fn3()
output:
> [LOG]: "branch 1" // correct behavior
This was very confusing if you overthink it, turns out you can simply understand it as "You must have 2+ args in order to do overloads", that is why the code sample [code 2.1] and [code 2.2] worked. Since:
- For the
[code 2.1] it had 1 and 3 args. Which fits the requirement mentioned documentation
- For
[code 2.2] it had 1 and 2. Which also fits the requirement in the docs.
- However, for
[code 2.3] it had 0 and 1. This does not fit the requirement of you should always have two or more signatures above the implementation of the function in the docs, that is why the tsc is complaining.
And this actually makes sense, since:
function fn3(): void;
function fn3(x?: string): void {
if (x === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x}`)
}
}
Is the same as just having defined one argument with ? optional param:
function fn3(x?: string): void {
if (x === undefined) {
console.log("branch 1")
} else {
console.log(`branch 2 -> ${x}`)
}
}
Incorrect: see @David's answer
### Answer to concern 2:
Overload method declaration only works for method with 2 and 2+ arguments, and a method with a body is required to handle all the cases you declared for the overloads. Also, this does not work with methods that have 0 to 1 arguments, tsc will complain Expected 0 arguments, but got 1.ts(2554) in that case. In the meantime, declare overload for methods with 0 and 1 arguments is redundant, since you can just declare a method with 1 optional param.