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
},
};