CodeOath
← All posts
TypeScript75 min total · 21 parts

TypeScript Fundamentals: Types, Interfaces, Generics, and Why It Catches Bugs Before Runtime

Contents — Part 10 of 21: Functions, Overloads, and this
Part 10 of 21 · ~1 min

Functions, Overloads, and this

Function types can be written standalone, useful for callbacks and higher-order functions:

type Comparator<T> = (a: T, b: T) => number;

function sortBy<T>(items: T[], compare: Comparator<T>): T[] {
  return [...items].sort(compare);
}

Overloads let a single function name have multiple valid call signatures, useful when a function's return type genuinely depends on which shape of arguments was passed — something a union parameter type alone can't express cleanly:

function makeElement(tag: "a"): HTMLAnchorElement;
function makeElement(tag: "img"): HTMLImageElement;
function makeElement(tag: string): HTMLElement {
  return document.createElement(tag);
}

const link = makeElement("a");   // typed as HTMLAnchorElement
const img = makeElement("img");  // typed as HTMLImageElement

Only the last signature (the "implementation signature") has a body, and it must be compatible with every overload above it — callers never see it directly; they only see the specific overloads.

TypeScript can also type this inside a standalone function, catching a call made with the wrong receiver:

interface Clickable {
  label: string;
  onClick(this: Clickable): void;
}

const button: Clickable = {
  label: "Save",
  onClick() {
    console.log(this.label); // `this` is checked as Clickable here
  },
};