Functional Programming in TypeScript: Basics and Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Functional Programming in TypeScript: From Theory to Practice

Functional Programming (FP) is a paradigm based on computation through the application of functions and avoiding state mutation. TypeScript, being a strictly typed superset of JavaScript, provides powerful tools for implementing FP. In this article, we will break down the key concepts of functional programming in TypeScript with practical code examples.



1. Pure Functions and Immutability

A pure function is a function that:

  • Always returns the same result for the same arguments (determinism).
  • Has no side effects (does not modify external variables, does not perform I/O operations).
// Pure functionconst add = (a: number, b: number): number => a + b;

// Impure function (side effect — modifying external state)let counter = 0;const increment = (): number => ++counter;

Immutability means that data is not changed, but new copies are created. In TypeScript, you can use the spread operator and non-mutating methods:

interface User {  name: string;  age: number;}

// Mutation (bad)const updateAgeMutable = (user: User, newAge: number): void => { user.age = newAge;};

// Immutable approach (good)const updateAgeImmutable = (user: User, newAge: number): User => ({ ...user, age: newAge,});

Immutability simplifies debugging and testing, as data is always predictable.



2. Higher-Order Functions and Closures

Higher-order functions are functions that take other functions as arguments or return them. An example is the Array.map method:

const numbers: number[] = [1, 2, 3, 4];const doubled: number[] = numbers.map((x) => x * 2);console.log(doubled); // [2, 4, 6, 8]

A closure is a function that "remembers" variables from its outer scope even after the outer function has finished executing:

const createMultiplier = (factor: number): ((x: number) => number) => {  return (x: number) => x * factor;};

const double = createMultiplier(2);const triple = createMultiplier(3);

console.log(double(5)); // 10console.log(triple(5)); // 15

Closures are widely used to create functions with partial application of arguments (currying).



3. Function Composition and Currying

Function composition is combining several functions into one, where the output of one function is the input for the next. In TypeScript, you can write a utility for composition:

const compose = <T, R>(...fns: Array<(arg: T) => T>) => (arg: T): T =>   fns.reduceRight((acc, fn) => fn(acc), arg);

// Example: string transformationconst toUpperCase = (s: string): string => s.toUpperCase();const exclaim = (s: string): string => `${s}!`;const shout = compose(toUpperCase, exclaim);

console.log(shout("hello")); // "HELLO!"

Currying is transforming a function with multiple arguments into a sequence of functions with a single argument:

// Regular functionconst add = (a: number, b: number): number => a + b;

// Curried versionconst curriedAdd = (a: number) => (b: number): number => a + b;

const add5 = curriedAdd(5);console.log(add5(3)); // 8

Currying simplifies creating specialized functions and improves code reusability.



4. Monads and Working with Option/Maybe

A monad is a functional programming pattern that encapsulates a value and provides an interface for transforming it. One of the simplest monads is Maybe (or Option), which safely handles n

Blogs

Book Recommendations