Functional Programming in TypeScript: From A to Z

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 A to Z



Functional programming (FP) is a paradigm that treats computation as the evaluation of mathematical functions and avoids changing state and mutable data. TypeScript, being a strictly typed extension of JavaScript, provides an excellent foundation for applying FP principles, allowing you to write more predictable, testable, and reliable code. In this article, we will break down the key concepts of FP in TypeScript, starting from the basics and moving to advanced techniques.



Basic Principles of Functional Programming



Before diving into code, it is important to understand the fundamental principles on which FP is built.



Pure Functions

A pure function is a function that:

  • Always returns the same result for the same arguments (deterministic).
  • Has no side effects (does not modify external variables, does not output data to the console, does not send requests).

Example of a pure function:

function add(a: number, b: number): number {  return a + b;}

// Always predictableconsole.log(add(2, 3)); // 5console.log(add(2, 3)); // 5


Example of an impure function:

let counter = 0;function increment(): number {  return ++counter; // Side effect: modifying an external variable}


Immutability

Data should not be changed after creation. Instead of modifying an existing object, we create a new copy with the necessary changes.

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

const user: User = { name: "Alice", age: 30 };

// Bad (mutation)user.age = 31;

// Good (immutability)const updatedUser: User = { ...user, age: 31 };

console.log(user); // { name: "Alice", age: 30 } - original unchangedconsole.log(updatedUser); // { name: "Alice", age: 31 }


Composition and Currying in TypeScript

Function composition is the process of combining several simple functions to create a more complex one. Currying is the transformation of a function with multiple arguments into a sequence of functions, each taking a single argument.



Function Composition

A typed implementation of composition in TypeScript:

function compose<A, B, C>(f: (x: B) => C, g: (x: A) => B): (x: A) => C {  return (x: A) => f(g(x));}

const toUpperCase = (str: string): string => str.toUpperCase();const exclaim = (str: string): string => `${str}!`;

const shout = compose(exclaim, toUpperCase);console.log(shout("hello world")); // "HELLO WORLD!"


Currying

Example of a curried function:

function multiply(a: number): (b: number) => number {  return (b: number) => a * b;}

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

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

Currying makes functions more flexible and convenient for composition.



Working with Monads: Option and Result

Monads are a powerful pattern for handling side effects and error handling without breaking function purity. In TypeScript, the Option (or Maybe) and Result (or Either) monads are often used.



Option (Maybe) Monad

Allows safe work with values that may be null or undefined.

type Option<T> = T | null;

function safeDivide(a: number, b: number): Option<number> { return b === 0 ? null : a / b;}

function getValue(value: Option<number>): string { if (value === null) { return "Error: division by zero"; } return `Result: ${value}`;}

console.log(getValue(safeDivide(10, 2)

Blogs

Book Recommendations