Skip to main content
Software EngineeringTutorials

Functional Programming in JavaScript: Practical Patterns

Apply functional programming principles to everyday JavaScript: immutability, pure functions, composition, and practical uses of map/filter/reduce.

Mbeah Essilfie

Mbeah Essilfie

May 18, 2026 at 06:04 AM

10 min read 1908 views
Functional Programming in JavaScript: Practical Patterns

FP in JavaScript: Be Practical

You don't need monads or category theory to benefit from functional programming. Start with three principles: pure functions, immutability, and composition.

Pure Functions

A pure function: same input → same output, no side effects.

// ❌ Impure — depends on external state
let taxRate = 0.2
function calculatePrice(amount: number) {
  return amount * (1 + taxRate) // relies on external variable
}

// ✅ Pure — all inputs explicit
function calculatePrice(amount: number, taxRate: number) {
  return amount * (1 + taxRate)
}

Why it matters: pure functions are trivial to test, cache, and parallelize.

Immutability

Never mutate — create new values:

// ❌ Mutation
function addItem(cart: Item[], item: Item) {
  cart.push(item) // modifies the original!
  return cart
}

// ✅ Immutable
function addItem(cart: Item[], item: Item): Item[] {
  return [...cart, item] // new array
}

// ✅ For objects
function updateUser(user: User, changes: Partial<User>): User {
  return { ...user, ...changes }
}

Function Composition

Build complex operations from simple ones:

const pipe = <T>(...fns: Array<(arg: T) => T>) =>
  (value: T): T =>
    fns.reduce((acc, fn) => fn(acc), value)

const processText = pipe(
  (s: string) => s.trim(),
  (s: string) => s.toLowerCase(),
  (s: string) => s.replace(/\s+/g, '-'),
  (s: string) => s.replace(/[^a-z0-9-]/g, ''),
)

processText('  Hello World! ')  // → "hello-world"
Composition lets you build complex behavior by snapping simple, tested functions together. Each piece is easy to understand and test in isolation.

Practical: Data Transformation Pipeline

interface Order {
  id: string
  items: Array<{ price: number; quantity: number }>
  status: 'pending' | 'shipped' | 'delivered'
  createdAt: Date
}

function getMonthlyRevenue(orders: Order[], month: number): number {
  return orders
    .filter(order => order.status !== 'pending')
    .filter(order => order.createdAt.getMonth() === month)
    .flatMap(order => order.items)
    .reduce((total, item) => total + item.price * item.quantity, 0)
}

Higher-Order Functions

Functions that take or return functions:

// Retry wrapper
function withRetry<T>(fn: () => Promise<T>, attempts = 3): () => Promise<T> {
  return async () => {
    for (let i = 0; i < attempts; i++) {
      try {
        return await fn()
      } catch (e) {
        if (i === attempts - 1) throw e
        await new Promise(r => setTimeout(r, 1000 * (i + 1)))
      }
    }
    throw new Error('Unreachable')
  }
}

const fetchWithRetry = withRetry(() => fetch('/api/data'))

When NOT to Go Functional

  • Performance-critical loops (allocations from spread/map add up)
  • When mutation is clearer (sorting an array in place)
  • When it makes code harder to read (over-composition)

FP is a tool, not a religion. Use it where it makes code clearer and more reliable.

JavaScriptTypeScriptArchitecture
Mbeah Essilfie

Written by Mbeah Essilfie

Fullstack Software Developer

Read next

The Complete Guide to Vue 3 Composables
12 min read

The Complete Guide to Vue 3 Composables

Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.

Mbeah EssilfieMbeah Essilfie
985