CodeWithMihir

TypeScript Tutorial

TypeScript Function Overloading Explained

Learn how function overloading works in TypeScript, including overload signatures, implementation signatures, practical examples, and common mistakes.

Welcome back! I am Mihir, and in this lesson we will learn function overloading in TypeScript.

Function overloading lets one function support multiple call patterns while still giving callers strong type checking.


The Problem

Sometimes a function can accept different types of input.

function format(value: string | number): string {
  if (typeof value === "string") {
    return value.trim();
  }

  return value.toFixed(2);
}

This works, but the function signature only says:

string | number

Overloads let you describe each valid call more clearly.


Basic Function Overload

function format(value: string): string;
function format(value: number): string;

function format(value: string | number): string {
  if (typeof value === "string") {
    return value.trim();
  }

  return value.toFixed(2);
}

The first two lines are overload signatures.

The last function is the implementation signature.


Calling an Overloaded Function

const name = format("  Mihir  ");
const price = format(99.5);

Both calls are valid.

Invalid:

format(true);

TypeScript rejects this because there is no overload for boolean.


Overloads with Different Return Types

Overloads are especially useful when different inputs return different types.

function getValue(value: string): string;
function getValue(value: number): number;

function getValue(value: string | number): string | number {
  return value;
}

const username = getValue("mihir");
const score = getValue(100);

TypeScript knows:

username; // string
score; // number

Implementation Must Handle All Overloads

The implementation signature must be broad enough to support every overload.

function double(value: string): string;
function double(value: number): number;

function double(value: string | number): string | number {
  if (typeof value === "string") {
    return value + value;
  }

  return value * 2;
}

The implementation uses string | number because it must handle both cases.


Overloads with Multiple Parameters

function createUser(name: string): { name: string };
function createUser(name: string, age: number): { name: string; age: number };

function createUser(name: string, age?: number) {
  if (age === undefined) {
    return { name };
  }

  return { name, age };
}

Usage:

const userOne = createUser("Mihir");
const userTwo = createUser("Mihir", 28);

Overloads vs Union Parameters

Use a union when the return type does not depend heavily on the input.

function printId(id: string | number): void {
  console.log(id);
}

Use overloads when the call pattern affects the return type.

function parseInput(value: string): string[];
function parseInput(value: number): number[];

function parseInput(value: string | number): string[] | number[] {
  if (typeof value === "string") {
    return value.split(",");
  }

  return [value];
}

Common Mistake

Do not expose an overload that the implementation cannot safely support.

function convert(value: string): number;
function convert(value: boolean): number;

function convert(value: string | boolean): number {
  return Number(value);
}

This compiles, but the boolean overload may be confusing for callers. Overloads should describe useful and intentional behavior.


Quick Recap

  • Function overloads describe multiple valid ways to call one function.
  • Overload signatures are written above the implementation.
  • The implementation signature must handle every overload.
  • Use overloads when different inputs produce different return types.
  • Use unions for simpler cases.

Next up, we move into Type Aliases →, where you will learn how to give reusable names to object shapes, unions, function types, and more.