Overview

5 Function Types

This chapter advances from basic and algebraic data types to function types, showing how typing functions unlocks more expressive, reusable designs. It explains function signatures (argument and return types), naming function types, and treating functions as first-class values that can be stored, passed, and returned. With this capability, familiar constructs become simpler and safer to express.

Several practical applications follow. The strategy pattern is distilled from interfaces and classes to a single function type plus a few concrete functions, reducing boilerplate while preserving the pattern’s intent. State machines move from enum-and-switch implementations to a pluggable function property that represents the current state’s behavior, with transitions implemented by reassigning that function; an alternative uses sum types to make states and transitions explicit. The chapter also shows how plugging functions into class properties enables easy runtime behavior changes (for example, a configurable greeter).

The chapter then introduces lazy values—deferring expensive work by passing functions (often lambdas) that compute results only when needed—and demonstrates how this keeps code lean and efficient. It builds up higher-order functions, focusing on three foundational data-processing tools: map (transform each item), filter (keep items satisfying a predicate), and reduce (combine items into a single value using an initial seed). It discusses implementation nuances such as reduce’s initial value and order, notes common library equivalents across languages, and connects these ideas to functional programming, showing how typed functions bring FP techniques into TypeScript with minimal ceremony.

Strategy pattern made up of an IStrategy interface, ConcreteStrategy1 and ConcreteStrategy2 implementations, and a Context which uses the algorithms through the IStrategy interface
Strategy pattern made up of a Context which uses a function, either concreteStrategy1() or concreteStrategy2()
Two TypeScript (.ts) files containing code samples which should be inlined in the Markdown document between ```ts and ``` markers. The <!-- … --> comments annotate the code samples for my script.
Text processing state machine with the three states (text processing, marker processing, code processing) and transitions between the states based on input. Text processing is the initial state or start state.
Squaring numbers and getting string lengths are very different scenarios but the overall structure of the transformation is the same: take an input array, apply a function, produce an output array.
Keeping only even numbers and strings with length 5 share a common structure: we traverse the input, apply the filter, and output the items for which the filter returns true.
Common structure of combining the numbers in a number array and strings in a string array. In the first case, the initial value is 1 and the combination we apply is multiplication with each item. In the second case, the initial value is “” and the combination we apply is concatenation with each item.
Combining an array of strings with the operation “first letter of both strings” gives us different results when applied from left to right and when applied from right to left. In the first case we start with empty string and “apple”, then “a” and “orange”, then “ao” and “peach” giving us “ap”. In the second case we start with “peach” and empty string, then “orange” and “p” giving “op”, then “apple” and “op” giving “ao”.

FAQ

What is a function type (signature) in TypeScript?A function type (also called a signature) is defined by the types of its parameters and its return type. Two functions with the same parameter types and return type have the same function type. Example: (x: number, y: number) => number describes “function from (number, number) to number.”
How do function types simplify the Strategy pattern?Instead of defining an interface and multiple classes, define a type alias for the algorithm’s function and pass/choose functions directly. Example:
type WashingStrategy = (car: Car) => void;

function standardWash(car: Car) { /* ... */ }
function premiumWash(car: Car) { /* ... */ }

class CarWash {
  service(car: Car, premium: boolean) {
    const strategy: WashingStrategy = premium ? premiumWash : standardWash;
    strategy(car);
  }
}
This removes boilerplate while preserving the pattern’s intent: pick an algorithm at runtime.
What does “first-class functions” mean and why does it matter?First-class functions can be: - Stored in variables and properties - Passed as arguments and returned from functions - Checked and constrained by the type system This enables patterns like functional strategies, pluggable behavior, lazy values, and higher-order algorithms.
How can I implement a state machine without switch statements?Use a function-typed property for the current state handler and reassign it on transitions:
class TextProcessor {
  private result: string[] = [];
  private processLine: (line: string) => void = this.textState;

  private textState(line: string) {
    this.result.push(line);
    if (line.startsWith("<!--")) this.processLine = this.markerState;
  }
  private markerState(line: string) {
    this.result.push(line);
    if (line.startsWith("```ts")) this.processLine = this.codeState;
  }
  private codeState(line: string) {
    if (line.startsWith("```")) {
      this.result.push(line);
      this.processLine = this.textState;
    }
  }
}
The current “state” is the function stored in processLine.
When should I prefer interfaces/classes over function types for strategies or states?Prefer interfaces/classes when: - The strategy/state must expose multiple operations (not just one function) - You need to bundle and evolve shared state with behavior - You want explicit, named types for many states and transition rules (clarity, documentation) Function types are ideal when the behavior is a single computation with minimal extra state.
What are lazy values and how do I implement them?Lazy values defer expensive computation by passing a function that produces the value only when needed:
function chooseMyRide(bike: Bike, car: () => Car): Bike | Car {
  return isItRaining() ? car() : bike;
}

chooseMyRide(new Bike(), () => new Car()); // Car built only if used
This avoids paying the cost unless the value is actually required.
What is a lambda (anonymous function) in TypeScript?A lambda is an anonymous function, commonly written with arrow syntax. Example: (x: number, y: number) => x + y. Lambdas are ideal for small, one-off pieces of logic passed to higher-order functions or APIs.
What are map, filter, and reduce, and what are their TypeScript signatures?- map: transform each item
function map<T, U>(items: T[], f: (item: T) => U): U[]
- filter: keep items where predicate is true
function filter<T>(items: T[], pred: (item: T) => boolean): T[]
- reduce: combine items into a single value
function reduce<T>(items: T[], init: T, op: (a: T, b: T) => T): T
Usage examples:
map([1,2,3], n => n*n)               // [1,4,9]
filter([1,2,3,4], n => n % 2 === 0)   // [2,4]
reduce([1,2,3,4], 1, (a,b) => a*b)    // 24
Why does reduce need an initial value and when does order matter?An initial value handles empty inputs and defines the starting accumulator. For non-associative operations (or when the initial value isn’t a true identity), left-to-right vs right-to-left can change the result. Many libraries also provide reduceRight. When the operation and identity form a monoid (associative + identity), order doesn’t change the outcome.
How can sum (union) types help with state machines?Model each state as a distinct type and represent the machine as a union of those types. Each state’s process returns the next state’s type (or itself), making possible states and transitions explicit and type-checked. This improves clarity and safety at the cost of more boilerplate compared to the function-property approach.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Programming with Types ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Programming with Types ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Programming with Types ebook for free