

3 Ways To Write Function Overloads With JSDoc & TypeScript
source link: https://austingil.com/typescript-function-overloads-with-jsdoc/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

I like TypeScript, but I prefer the JSDoc syntax for writing it. That should be obvious if you’ve read any of my JavaScript articles, especially Get Started With TypeScript the Easy Way.
So far, I haven’t run into any scenarios where I can’t use JSDoc to accomplish the same functionality as TypeScript.
It wasn’t until I needed to implement the type definition for JavaScript function overloads that I seriously started to question that.
What Is Function Overloading?
Feel free to skip this if you already know, but if you don’t let’s first understand what function overloading is.
Function overloads are when you define the same function more than once in order to capture different functionality.
Here’s a contrived example. Let’s say we wanted to create a function called double
. It takes one parameter. If the parameter is a number, it would multiply it by two and return the result. If it’s a string, it will concatenate that string to itself and return that.
It’s a silly example, but it might look like this:
function double(input) {
return input * 2
}
function double(input) {
return input + input
}
Beautiful!
There’s just one problem. JavaScript doesn’t support function overloading. Instead, we’d have to do something like this:
function double(input) {
if (typeof input === 'number') {
return input * 2
}
return input + input
}
A Naive Solution
If we want to write the type definition for this function, it’s a little complicated. We know the input can be either a string
or a number
, and the output is kind of the same thing.
We could accomplish that with a “union” type. Unions allow us to define a type as being either “this” or “that”. In our case, either a string
or a number
.
Here we’ll use JSDoc’s @param
and @returns
keywords to assign the input and output to a union of string
and number
.
/**
* @param {string | number} input
* @returns {string | number}
*/
function double(input) {
if (typeof input === 'number') {
return input * 2
}
return input + input
}
The problem here is that no matter what, when we call our function, we will always get back a union.

What we really want is to get back one specific type based on what the input is. That’s where function overloads come in.
Defining Function Overloads In JSDoc
TypeScript already has documentation dedicated to function overloads. In their example, they show how a simple function can be documented:
function add(a:string, b:string):string;
function add(a:number, b:number): number;
function add(a: any, b:any): any {
return a + b;
}
add("Hello ", "Steve"); // returns "Hello Steve"
add(10, 20); // returns 30
Notice how the same function is defined three times. Twice for the type definitions, and once for the functionality.
Unfortunately, the same cannot be said for JSDoc.
After a lot of searching, I finally found a solution that seems to work well enough. We can define a variable whose value is an an anonymous function. Just above that variable definition, we can use JSDocs @type
keyword to define the type for that variable, and within that type definition, we can describe our function overloads.
In this case, we want to describe two arrow functions. One that takes an input with a type of number
and whose return type is a number
, and one that takes an input with a type of string
and whose return type is a string
:
/**
* @type {{
* (input: number) => number;
* (input: string) => string;
* }}
*/
const double = (input) => {
if (typeof input === 'number') {
return input * 2
}
return input + input
}
The above example uses an arrow function. That may not be appropriate for scenarios where scope is a concern. Fortunately, we can accomplish the same with a function expression:
/**
* @type {{
* (input:number) => number;
* (input:string) => string;
* }}
*/
const double = function(input) {
if (typeof input === 'number') {
return input * 2
}
return input + input
}
As a result, we will see different type definitions for our functions based on whether the input is a number or a string.
When our input is a number, our function’s type definition shows that it returns a number.

When our input is a string, our function’s type definition shows that it returns a string.

More importantly, we get the desired results for our variable’s type definition. When the input is a number, our variable is a number.

When the input is a string, our variable is a string.

The syntax for defining function overloads is a bit strange, but it works well enough in practice. The only caveat I found is that it relies on assigning the function to a variable. It does not work with function declarations (ie, function double(input) { /* ... */ }
).
To be honest, I can’t think of a scenario where you must use a function declaration and cannot use a function expression, but if you really need a solution for that, there is a workaround.
TypeScript also offers generics which you can combine with conditional types to determine the input type and conditionally return a specific type based on what the input is. All of that can even work with JSDoc thanks to the @template
keyword (which is not well documented).
Applying that to our example above would look like this:
/**
* @template numOrStr
* @param {numOrStr} input
* @returns {numOrStr extends number ? number : string}
*/
function double(input) {
if (typeof input === 'number') {
return input * 2
}
return input + input
}
We define our generic as numOrString
, apply that as the input type, then in the return type, we check whether the input type extends a number type. If it does, the return value is a number type. If not, it’s a string type.
Closing
TypeScript is great, and JSDoc is great, but once in a while, the documentation for complex things is sparse.
I wrote this article because I’m sure that I will need to look up how to do this in the future and I’d rather not go through that whole treasure hunt again. Instead it can live all in one place here, and maybe help out other folks like you.
We covered a lot of complex TypeScript things like unions, overloads, generics, and dynamic types. I hope I was able to explain them in a clear way. And if you’re new to TypeScript, I’d recommend going to read my beginners guide.
Thank you so much for reading. If you liked this article, please share it, and if you want to know when I publish more articles, sign up for my newsletter or follow me on Twitter. Cheers!
Originally published on austingil.com.
Recommend
-
71
README.md Overload Return An annotation and bytecode rewriter that creates overloads of methods which vary only by return type. This is not legal in so...
-
6
Jimmy Byrd on Twitter: "FsToolkit.ErrorHandling 2.5.0 is out! Added Async, Task and Job overloads for the relevant Option CEs #dotnet #dotnetcore #fsharp https://t.co/StxJDxFYA0"Don’t miss what’s happeningPeople on Twitter are th...
-
9
-
8
Write Function Overloads using JSDoc and TypeScriptOctober 22nd 2021 new story6
-
6
TypeScript vs JSDoc JavaScriptNovember 22, 2021 · 6 min readThere's a debate to be had about whether using JavaScript or TypeScript leads to better outcomes when building a project. The introduction of using JSDoc annotati...
-
12
No Overloads with Top-Level Statements Arrays in Azure Web App Configuration →
-
5
Using JSDOC-Based TypeScript Get Started Choose your editor WebStorm, Rider Partial support, not enough intelli hints Toggle on TypeScript language service VSCode Ful...
-
5
JavaScript Weekly Issue 638 Easy to unsubscribe at any time. Your e-mail address is safe — here's
-
5
这听起来是不是很耳熟:你想写一个小型脚本,不管是为页面、命令行工具,还是其他什么类型。你从JavaScript开始,直到你想起写代码时没有类型是多么痛苦。所以你把文件从.js重命名为.ts。然后意识到你已经打开了一个麻烦的玩意儿。如果你在为一个网站或一个库...
-
7
Jun 7th, 2023Is JSDoc Better than TypeScript?👇 Download Show
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK