runtime.boot

Full Stack Systems
Production Mindset

CodeWithMihir

Engineering thoughtful products from interface to infrastructure.

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.