100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
TypeScript

Function Overloads in TypeScript

Declare multiple callable signatures for a single function so callers get precise types for each usage pattern.

FunctionsIntermediate9 min readJul 8, 2026
Analogies

1. Introduction

Sometimes a single function needs to behave differently — and return different types — depending on the combination of arguments it receives. A naive union-typed parameter loses precision: callers get a broad return type no matter which arguments they actually passed. Function overloads solve this by letting you declare several specific call signatures for one implementation, so each valid call shape gets its own precise, checked type.

🏏

Cricket analogy: A single getDismissalDetails function taking a union parameter would return a vague result no matter the input, but overloads let "bowled" return bowler stats and "caught" return fielder stats, each precisely typed.

Overloads are common in library APIs, such as a createElement function that returns a HTMLDivElement when called with "div" and an HTMLSpanElement when called with "span", all backed by one runtime implementation.

🏏

Cricket analogy: A ground's DRS review system behaves differently for an lbw appeal versus a caught-behind appeal, all backed by one umpire's decision engine, just as createElement("div") and createElement("span") share one runtime implementation with different return types.

2. Syntax

typescript
// Overload signatures (no body)
function parseInput(value: string): string[];
function parseInput(value: number): number[];

// Implementation signature (must cover both overloads, has a body)
function parseInput(value: string | number): string[] | number[] {
  if (typeof value === "string") {
    return value.split(",");
  }
  return [value, value * 2];
}

const a = parseInput("a,b,c"); // string[]
const b = parseInput(5);       // number[]

3. Explanation

You write two or more overload signatures — declarations with no function body — followed by exactly one implementation signature that has a body. The implementation signature's parameter and return types must be broad enough to be compatible with every overload it backs, since the implementation is the code that actually runs for all call shapes. Only the overload signatures are visible to callers; when you call parseInput, TypeScript matches your arguments against the overload list top-to-bottom and reports the corresponding return type.

🏏

Cricket analogy: A DRS protocol lists separate rulings for lbw and caught appeals, but only one umpire actually makes the final call each time; likewise you write multiple overload signatures with no body and one implementation signature with a body.

The implementation signature itself is not directly callable from outside — if you tried to call parseInput with a type that only matches the implementation signature but no overload (like boolean), TypeScript would reject the call even though the implementation's body might technically handle it, because callers only see the declared overloads.

🏏

Cricket analogy: Even if the umpire's actual review process could technically handle a runout appeal too, if the DRS menu never lists that option, players can't request it; likewise a caller can't invoke parseInput with a type only the implementation would accept.

Order matters: TypeScript checks overload signatures top to bottom and uses the first one that matches the call's argument types, so put more specific overloads before more general ones to avoid an earlier, looser overload accidentally matching first.

A common gotcha: you cannot call the function using the implementation signature's parameter type directly. For example, calling parseInput(true) fails to compile even though the body only checks typeof value === "string" and would otherwise fall through — because boolean matches neither the string nor the number overload, TypeScript rejects the call before the implementation ever runs.

4. Example

typescript
function makeDate(timestamp: number): Date;
function makeDate(year: number, month: number, day: number): Date;
function makeDate(yearOrTimestamp: number, month?: number, day?: number): Date {
  if (month !== undefined && day !== undefined) {
    return new Date(yearOrTimestamp, month - 1, day);
  }
  return new Date(yearOrTimestamp);
}

const d1 = makeDate(2024, 3, 15);
console.log(d1.getFullYear(), d1.getMonth(), d1.getDate());

const d2 = makeDate(0);
console.log(d2.getFullYear());

5. Output

text
2024 2 15
1970

6. Key Takeaways

  • Overload signatures have no body; they declare the valid call shapes callers can use.
  • Exactly one implementation signature follows the overloads, carries the body, and must be compatible with all of them.
  • The implementation signature is not directly callable — only the declared overloads are visible to callers.
  • TypeScript matches calls against overloads in the order they are written, so specific overloads should come first.
  • Overloads give precise per-call-shape return types that a single union-typed signature cannot express.
  • A call whose arguments match only the implementation signature (not any overload) is rejected at compile time.

Practice what you learned

Was this page helpful?

Topics covered

#TypeScript#TypeScriptProgrammingStudyNotes#Programming#FunctionOverloadsInTypeScript#Function#Overloads#Syntax#Explanation#Functions#StudyNotes#SkillVeris