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

推荐订阅源

Cloudbric
Cloudbric
T
Threat Research - Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
P
Privacy & Cybersecurity Law Blog
H
Help Net Security
云风的 BLOG
云风的 BLOG
G
GRAHAM CLULEY
Spread Privacy
Spread Privacy
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
Arctic Wolf
Project Zero
Project Zero
Engineering at Meta
Engineering at Meta
P
Privacy International News Feed
Blog — PlanetScale
Blog — PlanetScale
Stack Overflow Blog
Stack Overflow Blog
M
MIT News - Artificial intelligence
The Register - Security
The Register - Security
Recorded Future
Recorded Future
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
PCI Perspectives
PCI Perspectives
Recent Announcements
Recent Announcements
Martin Fowler
Martin Fowler
A
About on SuperTechFans
W
WeLiveSecurity
GbyAI
GbyAI
V
Vulnerabilities – Threatpost
The GitHub Blog
The GitHub Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Check Point Blog
Y
Y Combinator Blog
月光博客
月光博客
Scott Helme
Scott Helme
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
U
Unit 42
G
Google Developers Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Threatpost
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google Online Security Blog
Google Online Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
Hugging Face - Blog
Hugging Face - Blog
MongoDB | Blog
MongoDB | Blog
博客园 - 司徒正美

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?