惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
Webroot Blog
Webroot Blog
AI
AI
P
Palo Alto Networks Blog
Attack and Defense Labs
Attack and Defense Labs
WordPress大学
WordPress大学
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
Spread Privacy
Spread Privacy
T
Tor Project blog
罗磊的独立博客
小众软件
小众软件
S
Security Affairs
酷 壳 – CoolShell
酷 壳 – CoolShell
量子位
Apple Machine Learning Research
Apple Machine Learning Research
T
Threatpost
NISL@THU
NISL@THU
博客园_首页
PCI Perspectives
PCI Perspectives
大猫的无限游戏
大猫的无限游戏
IT之家
IT之家
N
News and Events Feed by Topic
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Forbes - Security
Forbes - Security
博客园 - 叶小钗
D
Darknet – Hacking Tools, Hacker News & Cyber Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
L
LINUX DO - 热门话题
T
Threat Research - Cisco Blogs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
腾讯CDC
Security Latest
Security Latest
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
The Cloudflare Blog
A
About on SuperTechFans
爱范儿
爱范儿
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
TaoSecurity Blog
TaoSecurity Blog
宝玉的分享
宝玉的分享
G
GRAHAM CLULEY
雷峰网
雷峰网
F
Full Disclosure
I
Intezer
Cloudbric
Cloudbric
博客园 - 三生石上(FineUI控件)
U
Unit 42

Dmitri Pavlutin Blog

Pure Functions in JavaScript: A Beginner's Guide Record Type in TypeScript: A Quick Intro How to Write Comments in React: The Good, the Bad and the Ugly 4 Ways to Create an Enum in JavaScript React forwardRef(): How to Pass Refs to Child Components TypeScript Function Types: A Beginner's Guide How to Use v-model to Access Input Values in Vue Mastering Vue refs: From Zero to Hero Environment Variables in JavaScript: process.env 5 Must-Know Differences Between ref() and reactive() in Vue How to Destructure Props in Vue (Composition API) Triangulation in Test-Driven Development How to Use nextTick() in Vue Programming to Interface Vs to Implementation A Smarter JavaScript Mapper: array.flatMap() Array Grouping in JavaScript: Object.groupBy() How to Access ES Module Metadata using import.meta JSON Modules in JavaScript How to Trim Strings in JavaScript How to Debounce and Throttle Callbacks in Vue How to Show/Hide Elements in Vue Sparse vs Dense Arrays in JavaScript How to Fill an Array with Initial Values in JavaScript Covariance and Contravariance in TypeScript What are Higher-Order Functions in JavaScript? How to Use TypeScript with React Components Index Signatures in TypeScript How to Use React useReducer() Hook unknown vs any in TypeScript A Guide to React Context and useContext() Hook How to Use Promise.any() 2 Ways to Remove a Property from an Object in JavaScript 'return await promise' vs 'return promise' in JavaScript How to Use Promise.allSettled() How to Use fetch() with JSON JavaScript Promises: then(f,f) vs then(f).catch(f) What is a Promise in JavaScript? How to Use Promise.all() A Simple Guide to Component Props in React Don't Stop Me Now: How to Use React useTransition() hook A Simple Explanation of JavaScript Variables: const, let, var ES Modules Dynamic Import How to Memoize with React.useMemo() How to Cleanup Async Effects in React Why Math.max() Without Arguments Returns -Infinity How to Debounce and Throttle Callbacks in React Don't Confuse Function Expressions and Function Declarations in JavaScript How to Use ES Modules in Node.js Solving a Mystery Behavior of parseInt() in JavaScript How to Use Array Reduce Method in JavaScript 3 Ways to Merge Arrays in JavaScript A Guide to Jotai: the Minimalist React State Management Library The Difference Between Values and References in JavaScript How to Implement a Queue in JavaScript A Helpful Algorithm to Determine "this" value in JavaScript React useRef() Hook Explained in 3 Steps 7 Interview Questions on "this" keyword in JavaScript. Can You Answer Them? How to Greatly Enhance fetch() with the Decorator Pattern 7 Interview Questions on JavaScript Closures. Can You Answer Them? What's a Method in JavaScript? array.sort() Does Not Simply Sort Numbers in JavaScript How to Solve the Infinite Loop of React.useEffect() The New Array Method You'll Enjoy: array.at(index) What's the Difference between DOM Node and Element? Why Promises Are Faster Than setTimeout()? Everything About Callback Functions in JavaScript How React Updates State 5 Mistakes to Avoid When Using React Hooks 5 Best Practices to Write Quality JavaScript Variables Type checking in JavaScript: typeof and instanceof operators 3 Ways to Check if a Variable is Defined in JavaScript React Forms Tutorial: Access Input Values, Validate, Submit Forms Prototypal Inheritance in JavaScript How to Timeout a fetch() Request How to Learn JavaScript If You're a Beginner A Simple Explanation of React.useEffect() A Simple Explanation of JavaScript Iterators How to Use React Controlled Inputs Everything about null in JavaScript How to Use Fetch with async/await Getting Started with Arrow Functions in JavaScript An Interesting Explanation of async/await in JavaScript Front-end Architecture: Stable and Volatile Dependencies Is it Safe to Compare JavaScript Strings? How to Access Object's Keys, Values, and Entries in JavaScript What Actually is a String in JavaScript? 3 Ways to Shallow Clone Objects in JavaScript (w/ bonuses) Checking if an Array Contains a Value in JavaScript JavaScript Event Delegation: A Beginner's Guide How to Parse URL in JavaScript: hostname, pathname, query, hash 3 Ways to Detect an Array in JavaScript How to Get the Screen, Window, and Web Page Sizes in JavaScript 3 Ways to Check If an Object Has a Property/Key in JavaScript How to Compare Objects in JavaScript Object.is() vs Strict Equality Operator in JavaScript Own and Inherited Properties in JavaScript 5 Differences Between Arrow and Regular Functions How to Use Object Destructuring in JavaScript Your Guide to React.useCallback() 5 JavaScript Scope Gotchas
TypeScript Function Overloading
Dmitri Pavlutin · 2021-11-18 · via Dmitri Pavlutin Blog

Most of the functions accept a fixed set of arguments.

But some functions can accept a variable number of arguments, arguments of different types, or could return different types depending on how you invoke the function.

To annotate such function TypeScript offers the function overloading feature. Let's see how function overloading works.

Table of Contents

  • 1. The function signature
  • 2. The function overloading
    • 2.1 Overload signatures are callable
    • 2.2 The implementation signature must be general
  • 3. Method overloading
  • 4. When to use function overloading
  • 5. Conclusion

1. The function signature

Let's consider a function that returns a welcome message using a person's name:


function greet(person: string): string {

return `Hello, ${person}!`;

}


The function above accepts 1 argument of type string: the name of the person.

Invoking the function is pretty simple:


greet('World'); // 'Hello, World!'


What about making greet() function more flexible? For example, make it additionally accept a list of persons to greet.

Such a function should accept a string or an array of strings as an argument, as well as return a string or an array of strings.

How to annotate such a function? There are 2 approaches.

The first approach is straightforward and involves modifying the function signature directly by updating the parameter and return types:

Here's how greet() looks after updating the parameter and return types:


function greet(person: string | string[]): string | string[] {

if (typeof person === 'string') {

return `Hello, ${person}!`;

} else if (Array.isArray(person)) {

return person.map(name => `Hello, ${name}!`);

}

throw new Error('Unable to greet');

}


Now you can invoke greet() in 2 ways:


greet('World'); // 'Hello, World!'

greet(['Jane', 'Joe']); // ['Hello, Jane!', 'Hello, Joe!']


Updating the function signature directly to support the multiple ways of invocation is a viable approach.

But you can take an alternative approach and define separately all the ways your function can be invoked. This approach in TypeScript is called function overloading.

2. The function overloading

The second approach is to use the function overloading feature. I recommend it when the function signature is relatively complex and has multiple types involved.

Putting the function overloading in practice requires defining a few overload signatures and one implementation signature.

The overload signature defines the parameter and return types of the function, and doesn't have a body.

A function can have multiple overload signatures: corresponding to the different ways you can invoke the function.

The implementation signature has more generic parameter and return types, but also has a body that implements the function.

There can be only one implementation signature.

Let's transform the function greet() to use the function overloading:


// Overload signatures

function greet(person: string): string;

function greet(persons: string[]): string[];

// Implementation signature

function greet(person: unknown): unknown {

if (typeof person === 'string') {

return `Hello, ${person}!`;

} else if (Array.isArray(person)) {

return person.map(name => `Hello, ${name}!`);

}

throw new Error('Unable to greet');

}


The greet() function has 2 overload signatures and one implementation signature.

Each overload signature describes one way the function can be invoked. In the case of greet() function, you can call it 2 ways: with a string argument, or with an array of strings as an argument.

The implementation signature function greet(person: unknown): unknown { ... } contains the proper logic of how the function works.

Now, as before, you can invoke greet() with the arguments of type string or array of strings:


greet('World'); // 'Hello, World!'

greet(['Jane', 'Joe']); // ['Hello, Jane!', 'Hello, Joe!']


2.1 Overload signatures are callable

While the implementation signature implements the function behavior, however, it is not directly callable. Only the overload signatures are callable.


greet('World'); // Overload signature is callable

greet(['Jane', 'Joe']); // Overload signature is callable

const someValue: unknown = 'Unknown';

// Type error: No overload matches this call.

greet(someValue); // Implementation signature is NOT callable


In the example above you cannot call greet() function with an argument of type unknown (greet(someValue)), even if the implementation signature accepts unknown argument.

2.2 The implementation signature must be general

Be aware that the implementation signature type should be generic enough to include the overload signatures.

Otherwise, TypeScript won't accept the overload signature as being incompatible.

For example, if you modify the implementation signature's return type from unknown to string:


// Overload signatures

function greet(person: string): string;

// Type error: This overload signature is not

// compatible with its implementation signature.

function greet(persons: string[]): string[];

// Implementation signature

function greet(person: unknown): string {

// ...

throw new Error('Unable to greet');

}


Then the overload signature function greet(persons: string[]): string[] is marked as being incompatible with function greet(person: unknown): string.

string return type of the implementation signature isn't general enough to be compatible with string[] return type of the overload signature.

3. Method overloading

In the previous examples, function overloading was applied to a regular function. But you can overload methods too!

During method overloading, both the overload signatures and implementation signature are now a part of the class.

For example, let's implement a Greeter class, with an overload method greet():


class Greeter {

message: string;

constructor(message: string) {

this.message = message;

}

// Overload signatures

greet(person: string): string;

greet(persons: string[]): string[];

// Implementation signature

greet(person: unknown): unknown {

if (typeof person === 'string') {

return `${this.message}, ${person}!`;

} else if (Array.isArray(person)) {

return person.map(name => `${this.message}, ${name}!`);

}

throw new Error('Unable to greet');

}

}


The Greeter class contains greet() overload method: 2 overload signatures describing how the method can be called, and the implementation signature containing the proper implementation.

Thanks to method overloading you can call hi.greet() in 2 ways: using a string or using an array of strings as argument.


const hi = new Greeter('Hi');

hi.greet('Angela'); // 'Hi, Angela!'

hi.greet(['Pam', 'Jim']); // ['Hi, Pam!', 'Hi, Jim!']


4. When to use function overloading

Function overloading, when used the right way, simplifies the use of functions that may be invoked in multiple ways. That is especially useful during autocomplete: you get listed all the possible overloadings as separate records.

TypeScript Function Overloading Autocomplete First Signature TypeScript Function Overloading Autocomplete Second Signature

However, there are situations when I'd recommend not using the function overloading, but rather sticking to the function signature.

For example, don't use the function overloading for optional parameters:


// Not recommended

function myFunc(): string;

function myFunc(param1: string): string;

function myFunc(param1: string, param2: string): string;

function myFunc(...args: string[]): string {

// implementation...

}


Using the optional parameters in the function signature should be enough:


// OK

function myFunc(param1?: string, param2?: string): string {

// implementation...

}


For more details check Function overloading Do's and Don'ts.

5. Conclusion

Function overloading in TypeScript lets you define functions that can be called in multiple ways.

Using function overloading requires defining the overload signatures: a set of functions with parameter and return types, but without a body. These signatures indicate how the function should be invoked.

Additionally, you have to write the proper implementation of the function (implementation signature): the parameter and return types, as well as the function body. Note that the implementation signature is not callable.

Aside from regular functions, methods in classes can be overload too.

How often do you use function overloading in TypeScript?