Skip to content

Core Concepts

Functions that always return the same output for the same input and have no side effects.

  • Deterministic - Same input always produces same output
  • No Side Effects - Doesn’t modify external state
  • No Dependencies - Doesn’t depend on external mutable state
// Pure function - deterministic and no side effects
function add(a: number, b: number): number {
return a + b;
}
function multiply(a: number, b: number): number {
return a * b;
}
// Impure - relies on external state
let count = 0;
function incrementCount(): number {
return ++count; // Side effect: modifies external state
}
// Impure - non-deterministic
function getCurrentTime(): Date {
return new Date(); // Returns different value each call
}
// Impure - has side effect
function logAndAdd(a: number, b: number): number {
console.log('Adding numbers'); // Side effect: I/O
return a + b;
}
// Pure version - inject dependencies
function formatTime(date: Date): string {
return date.toISOString();
}

✅ Easy to test - no setup required ✅ Easy to reason about - no hidden behavior ✅ Cacheable - memoization possible ✅ Parallelizable - no race conditions ✅ Composable - can be combined safely

Data that cannot be changed after creation. Instead of modifying data, create new data.

const numbers = [1, 2, 3];
// Bad - mutates original array
numbers.push(4);
numbers.sort();
numbers.splice(1, 1);
// Good - creates new arrays
const withFour = [...numbers, 4];
const sorted = [...numbers].sort();
const withoutSecond = numbers.filter((_, i) => i !== 1);
const user = { name: 'John', age: 30 };
// Bad - mutates object
user.age = 31;
user.email = 'john@example.com';
// Good - creates new object
const updatedUser = { ...user, age: 31 };
const withEmail = { ...user, email: 'john@example.com' };
const state = {
user: {
profile: {
name: 'John',
email: 'john@example.com'
},
settings: {
theme: 'dark'
}
}
};
// Bad - mutates nested object
state.user.profile.email = 'newemail@example.com';
// Good - immutable deep update
const newState = {
...state,
user: {
...state.user,
profile: {
...state.user.profile,
email: 'newemail@example.com'
}
}
};
const users = [
{ id: 1, name: 'John', active: true },
{ id: 2, name: 'Jane', active: false }
];
// Update single user immutably
const updatedUsers = users.map(user =>
user.id === 1
? { ...user, active: false }
: user
);
// Add new user
const withNewUser = [...users, { id: 3, name: 'Bob', active: true }];
// Remove user
const withoutUser = users.filter(user => user.id !== 2);

Functions as values that can be assigned to variables, passed as arguments, and returned from other functions.

// Assign function to variable
const greet = (name: string): string => `Hello, ${name}!`;
// Store functions in data structures
const operations = {
add: (a: number, b: number) => a + b,
subtract: (a: number, b: number) => a - b,
multiply: (a: number, b: number) => a * b
};
operations.add(5, 3); // 8
function executeOperation(
a: number,
b: number,
operation: (x: number, y: number) => number
): number {
return operation(a, b);
}
const sum = executeOperation(5, 3, (a, b) => a + b); // 8
const product = executeOperation(5, 3, (a, b) => a * b); // 15
// Array methods accept functions
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
function createMultiplier(factor: number) {
return (value: number): number => value * factor;
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
double(5); // 10
triple(5); // 15
// Creating validators
function createValidator(min: number, max: number) {
return (value: number): boolean => value >= min && value <= max;
}
const isValidAge = createValidator(0, 120);
const isValidPercentage = createValidator(0, 100);
isValidAge(25); // true
isValidPercentage(150); // false

Functions that take functions as arguments or return functions as results.

Transform each element in a collection:

const numbers = [1, 2, 3, 4, 5];
// Double each number
const doubled = numbers.map(n => n * 2);
// [2, 4, 6, 8, 10]
// Convert to objects
const users = ['John', 'Jane', 'Bob'].map((name, id) => ({
id,
name
}));
// [{ id: 0, name: 'John' }, ...]

Select elements that match a condition:

const numbers = [1, 2, 3, 4, 5];
// Get even numbers
const evens = numbers.filter(n => n % 2 === 0);
// [2, 4]
// Get numbers greater than 3
const greaterThanThree = numbers.filter(n => n > 3);
// [4, 5]
// Filter objects
const users = [
{ name: 'John', active: true },
{ name: 'Jane', active: false }
];
const activeUsers = users.filter(user => user.active);
// [{ name: 'John', active: true }]

Accumulate values into a single result:

const numbers = [1, 2, 3, 4, 5];
// Sum
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 15
// Product
const product = numbers.reduce((acc, n) => acc * n, 1);
// 120
// Group by property
const users = [
{ name: 'John', role: 'admin' },
{ name: 'Jane', role: 'user' },
{ name: 'Bob', role: 'admin' }
];
const byRole = users.reduce((acc, user) => {
const role = user.role;
return {
...acc,
[role]: [...(acc[role] || []), user]
};
}, {} as Record<string, typeof users>);
// { admin: [...], user: [...] }
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = numbers
.filter(n => n % 2 === 0) // Get even numbers
.map(n => n * 2) // Double them
.reduce((acc, n) => acc + n, 0); // Sum them
// 60
// Real-world example
const orders = [
{ id: 1, amount: 100, status: 'paid' },
{ id: 2, amount: 200, status: 'pending' },
{ id: 3, amount: 150, status: 'paid' }
];
const totalPaid = orders
.filter(order => order.status === 'paid')
.map(order => order.amount)
.reduce((sum, amount) => sum + amount, 0);
// 250

✅ Write pure functions whenever possible ✅ Use immutable data structures ✅ Compose small, focused functions ✅ Avoid side effects ✅ Use higher-order functions ✅ Keep functions deterministic ✅ Make dependencies explicit ✅ Return new data instead of mutating

❌ Mutate function arguments ❌ Depend on external mutable state ❌ Use global variables ❌ Have hidden side effects ❌ Mix pure and impure logic ❌ Modify objects in place ❌ Use void functions (prefer returns)