Want a faster signal before interviews? Take the Interview Ready Check →

DAY 27 OF 30

Function types and overloading

Yesterday, in one sentence

What do the utility types Record, ReturnType, Parameters and Awaited do?

Interview question

What is type narrowing, and how do type guards work?

A function type is a type that describes the shape of a function: it specifies its parameters and return type. It can be named and stored in a type aliasType Alias, which makes it reusable.

Attack below describes a function that takes one string parameter and returns a number. Annotating punch with it applies that shape, so target is a string and the function has to return a number.

// type TypeName = (param: type) => returnType
type Attack = (target: string) => number;

const punch: Attack = (target) => target.length;

target needs no annotation of its own, because TypeScript reads it from Attack. This is how callbacks and React props get typed.

In TypeScript a signature is a function's parameters and return type written without a body. Function overloading is writing more than one signature for the same function, so it can return a different type depending on what it is given. Those extra signatures are called overload signatures, and they sit directly above the function, each one a line ending in its return type.

function summon(name: string): string; // overload signature
function summon(count: number): string[]; // overload signature
// implementation signature: covers both of the above, not offered to callers
function summon(input: string | number): string | string[] {
  return typeof input === "number" ? Array(input).fill("hero") : input;
}

const one = summon("Logan"); // string
const team = summon(3); // string[]

At runtime there is only one function, the implementation, and every call goes to it. The overload signatures exist for TypeScript, which picks the first one matching the arguments and types the call from that.

Without them, both calls would return string | string[] and the caller would have to narrow.

The catch is that an argument has to match one overload signature on its own. A value typed as the union of both matches neither.

const input: string | number = 3;

summon(input);
// ❌ No overload matches this call

Check yourself

What is the type of result here?
function find(id: number): string
function find(id: string): string[]
function find(id: number | string): string | string[] {
  return typeof id === "number" ? "one" : ["many"]
}

const result = find("logan")
What is function overloading in TypeScript?