π Hey folks! In this post, I want to cover the topic of function overloading and explain why it's essential to understand this concept, and why you shouldn't be afraid to use it!
π Wiki Definition
Function overloading or method overloading is the ability to create multiple functions of the same name with different implementations. Calls to an overloaded function will run a specific version of that function appropriate to the context of the call.
π§ Wiki Definition Explanation
In short function overloading is a function with multiple call signatures. This means there are multiple ways to call a function.
Example:
Let's take a look at the example of a function which might be called in a few ways with different arguments
type ProductParams = {
promo?: boolean
isArchived?: boolean;
};
function getProducts(params: ProductParams) {
const url = new URL('/api/v1/products');
const urlParams = new URLSearchParams();
if ('promo' in params) {
urlParams.append('promo', params.promo.toString());
}
if ('isArchived' in params) {
urlParams.append('isArchived', params.isArchived.toString());
}
url.search = urlParams.toString();
return fetch(url.toString()).then(res => res.json());
}
You can call this function
in 2 different ways, with 2 different params. Our function will return from the API
products that might be promo
or archived
, or both promo
and archived
.
π Multiple Function Signatures
Typescript's approach to function overloading is quite distinct from some other languages. Instead of having multiple function definitions, TypeScript uses multiple function signatures followed by a single function body.
The type-checker evaluates these signatures from top to bottom. This order matters because TypeScript will use the first signature that matches the function call. For instance, if a function call can match two overloaded signatures, TypeScript will choose the one that's listed first. Therefore, it's advisable to list more specific signatures higher and keep broader ones below.
π» Let's implement the overloading for the example function
// Product Interfaces
interface PromoProducts {
//... some properties specific to promo products
}
interface ArchivedProducts {
//... some properties specific to archived products
}
interface PromoAndArchivedProducts {
//... some properties combining both promo and archived products
}
type ProductParams = {
promo?: boolean
isArchived?: boolean;
};
// Function overloading signatures
function getProducts(params: { promo: true }): Promise<PromoProducts>;
function getProducts(params: { isArchived: true }): Promise<ArchivedProducts>;
function getProducts(params: { promo: true, isArchived: true }): Promise<PromoAndArchivedProducts>;
function getProducts(params: ProductParams) {
const url = new URL('/api/v1/products');
const urlParams = new URLSearchParams();
if ('promo' in params) {
urlParams.append('promo', params.maxPrice.toString());
}
if ('isArchived' in params) {
urlParams.append('isArchived', params.isArchived.toString());
}
url.search = urlParams.toString();
return fetch(url.toString()).then(res => res.json());
}
After the implementation, we will be able to use our function
in different ways and have a type of safety in place.
π Finish
I recommend not being afraid to use function overloading. It's so helpful when you're providing type-rich interfaces and aiming for better code readability and safety.
Top comments (0)