Overloads

One Function, Multiple Type Signatures

Overloads

Function overloads let one implementation present multiple call signatures to callers. Use sparingly — generics are often better.

4 min read Level 2/5 #typescript#overloads#functions
What you'll learn
  • Declare overload signatures
  • Write the implementation signature
  • Recognize when generics replace overloads

Overloads let one function present multiple call signatures to callers — useful when the return type depends on which arguments were passed.

The Shape

// overload signatures (the "public" API)
function lookup(id: string): User;
function lookup(id: number): User;
function lookup(ids: string[]): User[];

// implementation signature (must be compatible with all overloads)
function lookup(arg: string | number | string[]): User | User[] {
  if (Array.isArray(arg)) return arg.map(id => fetchUser(id));
  return fetchUser(String(arg));
}

Two parts:

  1. Overload signatures — public APIs. Multiple declarations, no body.
  2. Implementation signature — the actual function. Its parameter / return types must be compatible with all overloads but are NOT visible to callers.

How Callers See It

const a = lookup("1");        // User
const b = lookup(2);           // User
const c = lookup(["1", "2"]); // User[]

lookup(true);
//     ~~~~  No overload matches this call.

TS only considers the overload signatures when type-checking calls. The implementation signature is internal.

When Generics Are Better

The example above is much cleaner as a generic:

function lookup<T extends string | number | string[]>(arg: T): T extends string[] ? User[] : User {
  // implementation
}

But honestly, even that is over-engineered. The simplest fix is two functions — lookup(id) and lookupMany(ids).

When Overloads Are the Right Tool

  • A function genuinely needs many signatures
  • You’re typing a JavaScript library whose runtime branches on arguments
  • The relationship between input and output types is hard to express with generics

For most application code, overloads are overkill. Two functions or a generic is usually clearer.

Method Overloads

Same idea on a class:

class Selector {
  query(id: string): HTMLElement | null;
  query(ids: string[]): HTMLElement[];
  query(arg: string | string[]): HTMLElement | HTMLElement[] | null {
    // implementation
    return null as any;
  }
}

Up Next

Typing callback functions cleanly.

Callback Types →