Higher-Order Functions
Functions that take functions, return functions, or both — the universal mechanism for abstraction in FP.
A higher-order function is a function that does at least one of two things:
- Takes a function as one of its arguments.
- Returns a function as its result.
That's the entire definition. The reason it's such a central concept is that passing behavior as a value is the most powerful form of abstraction a language can offer. Once you have it, you can replace whole categories of boilerplate with one-line calls.
Functions as values: the prerequisite
For higher-order functions to make sense, the language has to treat functions as first-class values — things you can store in variables, pass to other functions, return, and put in data structures. TypeScript inherited this from JavaScript, which inherited it from Scheme.
The fact that (x: number) => number is a type you can use
anywhere — as an array element, a record value, a parameter — is
what makes everything else in this chapter possible.
Functions that take functions: the classic combinators
The three you already know:
The key insight: map, filter, and reduce are one
implementation each. They work for any element type, any
transformation, any predicate, any accumulator. The behavior comes
from the function you pass in. The combinator provides the
plumbing.
Look at how much code you aren't writing:
mapreplaces "make a new array, loop, push transformed elements"filterreplaces "make a new array, loop, push elements that pass"reducereplaces "init an accumulator, loop, update accumulator"
This is the abstraction higher-order functions provide: the control flow is generic; the behavior is the parameter.
Building your own higher-order function
Whenever you find yourself writing the same plumbing twice with a different inner action, factor out a higher-order function.
The two functions share the same "wrap with timing" logic. The generic version is a single higher-order function:
timed is a higher-order function in both directions: it takes a
function (work) and runs it. We could equally well write a
"timing decorator" that returns a wrapped function.
Functions that return functions: factories
These returned functions are closures — they capture variables
from the enclosing scope (amount, n, re, message). Closures
are the technical mechanism that makes "function-returning-function"
useful.
Decorating: wrapping behavior
Returning a function that wraps another function is called a
decorator (the FP sense, unrelated to TypeScript's @decorator
syntax). It lets you add cross-cutting concerns — logging, timing,
retries, memoization, throttling — without modifying the inner
function.
The original add is unchanged and still usable. loggedAdd is a
new function with the same signature, plus the wrapper behavior.
That's the power of returning a function: you can layer new
behaviors on top of existing ones without rewriting them.
A library of useful higher-order functions
These come up so often they're worth memorizing.
Most "FP libraries" are, at their core, a curated collection of small higher-order functions like these. None of them are mysterious. All of them are six-line implementations.
Higher-order types: callbacks in signatures
When you write the type of a higher-order function in TypeScript, the function-as-value pattern shows up directly in the signature:
The type (a: A) => A describes what the function expects. The
compiler will reject any value that doesn't fit. That's how you get
both flexibility and safety: behavior is a parameter, but it's a
typed parameter.
A multi-file challenge
In validators.ts, implement three higher-order helpers:
every(...vs)— combine validators; returns the first error ornull.when(pred, v)— run validatorvonly whenpred(value)is true.mapInput(f, v)— adapt a validator from one type to another (e.g. validates.lengthwith a number validator).
Then validate the sample data in main.ts.
Expected output
error: too short
error: missing @
ok
Which of the following best describes a higher-order function?
A function that runs faster than other functions
A function defined at the top level of a module
A function that takes another function as an argument, returns a function, or both
A function annotated with TypeScript generics