Skip to content

compose

Compose multiple functions to create a function pipeline.

Basic Usage

typescript
import { compose } from 'radash'

const addOne = (x: number) => x + 1
const double = (x: number) => x * 2
const square = (x: number) => x * x

const composed = compose(addOne, double, square)
const result = composed(3)
// Calculation process: square(3) = 9, double(9) = 18, addOne(18) = 19
console.log(result) // 19

Syntax

typescript
function compose<T extends any[], U>(
  ...fns: ((...args: T) => U)[]
): (...args: T) => U

Parameters

  • ...fns (function[]): Array of functions to compose

Return Value

Returns a composed function that executes from right to left.

Examples

Basic Function Composition

typescript
import { compose } from 'radash'

const toUpperCase = (str: string) => str.toUpperCase()
const addExclamation = (str: string) => str + '!'
const repeat = (str: string) => str + str

const shout = compose(addExclamation, toUpperCase, repeat)
const result = shout('hello')
// Calculation process: repeat('hello') = 'hellohello', toUpperCase('hellohello') = 'HELLOHELLO', addExclamation('HELLOHELLO') = 'HELLOHELLO!'
console.log(result) // 'HELLOHELLO!'

Process Arrays

typescript
import { compose } from 'radash'

const filterEven = (arr: number[]) => arr.filter(x => x % 2 === 0)
const double = (arr: number[]) => arr.map(x => x * 2)
const sum = (arr: number[]) => arr.reduce((acc, x) => acc + x, 0)

const processNumbers = compose(sum, double, filterEven)
const result = processNumbers([1, 2, 3, 4, 5, 6])
// Calculation process: filterEven([1,2,3,4,5,6]) = [2,4,6], double([2,4,6]) = [4,8,12], sum([4,8,12]) = 24
console.log(result) // 24

Process Objects

typescript
import { compose } from 'radash'

const getUsers = (data: any) => data.users
const filterActive = (users: any[]) => users.filter(user => user.active)
const mapNames = (users: any[]) => users.map(user => user.name)
const joinNames = (names: string[]) => names.join(', ')

const getActiveUserNames = compose(joinNames, mapNames, filterActive, getUsers)

const data = {
  users: [
    { id: 1, name: 'Alice', active: true },
    { id: 2, name: 'Bob', active: false },
    { id: 3, name: 'Charlie', active: true },
    { id: 4, name: 'Diana', active: true }
  ]
}

const result = getActiveUserNames(data)
console.log(result) // 'Alice, Charlie, Diana'

Process Strings

typescript
import { compose } from 'radash'

const trim = (str: string) => str.trim()
const toLowerCase = (str: string) => str.toLowerCase()
const replaceSpaces = (str: string) => str.replace(/\s+/g, '-')
const addPrefix = (str: string) => `slug-${str}`

const createSlug = compose(addPrefix, replaceSpaces, toLowerCase, trim)

const result = createSlug('  Hello World  ')
// Calculation process: trim('  Hello World  ') = 'Hello World', toLowerCase('Hello World') = 'hello world', replaceSpaces('hello world') = 'hello-world', addPrefix('hello-world') = 'slug-hello-world'
console.log(result) // 'slug-hello-world'

Process Numbers

typescript
import { compose } from 'radash'

const add = (a: number) => (b: number) => a + b
const multiply = (a: number) => (b: number) => a * b
const square = (x: number) => x * x

const complexCalculation = compose(
  square,
  multiply(3),
  add(5)
)

const result = complexCalculation(2)
// Calculation process: add(5)(2) = 7, multiply(3)(7) = 21, square(21) = 441
console.log(result) // 441

Process Type Conversion

typescript
import { compose } from 'radash'

const parseNumber = (str: string) => parseInt(str, 10)
const addTen = (num: number) => num + 10
const double = (num: number) => num * 2
const toString = (num: number) => num.toString()

const processString = compose(toString, double, addTen, parseNumber)

const result = processString('15')
// Calculation process: parseNumber('15') = 15, addTen(15) = 25, double(25) = 50, toString(50) = '50'
console.log(result) // '50'

Process Validation and Transformation

typescript
import { compose } from 'radash'

const validateEmail = (email: string) => {
  if (!email.includes('@')) {
    throw new Error('Invalid email')
  }
  return email
}

const normalizeEmail = (email: string) => email.toLowerCase().trim()
const addDomain = (email: string) => email.includes('@') ? email : email + '@example.com'

const processEmail = compose(addDomain, normalizeEmail, validateEmail)

try {
  const result = processEmail('  USER@EXAMPLE.COM  ')
  console.log(result) // 'user@example.com'
} catch (error) {
  console.error('Error:', error.message)
}

Process Async Functions

typescript
import { compose } from 'radash'

const fetchUser = async (id: number) => {
  const response = await fetch(`/api/users/${id}`)
  return response.json()
}

const extractName = (user: any) => user.name
const toUpperCase = (name: string) => name.toUpperCase()

const getUserName = compose(toUpperCase, extractName, fetchUser)

// Note: Since fetchUser is async, this composed function will also return a Promise
const result = await getUserName(1)
console.log(result) // 'ALICE'

Process Conditional Logic

typescript
import { compose } from 'radash'

const isEven = (num: number) => num % 2 === 0
const double = (num: number) => num * 2
const addOne = (num: number) => num + 1

const processNumber = compose(
  (num: number) => isEven(num) ? double(num) : addOne(num),
  (num: number) => num * 3
)

console.log(processNumber(4)) // 24 (4 is even, double(4)=8, 8*3=24)
console.log(processNumber(5)) // 18 (5 is odd, addOne(5)=6, 6*3=18)

Process Error Handling

typescript
import { compose } from 'radash'

const safeParse = (str: string) => {
  try {
    return JSON.parse(str)
  } catch {
    return null
  }
}

const extractName = (data: any) => data?.name || 'Unknown'
const toUpperCase = (name: string) => name.toUpperCase()

const processData = compose(toUpperCase, extractName, safeParse)

console.log(processData('{"name": "alice"}')) // 'ALICE'
console.log(processData('invalid json')) // 'UNKNOWN'

Process Pipeline Operations

typescript
import { compose } from 'radash'

const filterNumbers = (arr: any[]) => arr.filter(x => typeof x === 'number')
const double = (arr: number[]) => arr.map(x => x * 2)
const sum = (arr: number[]) => arr.reduce((acc, x) => acc + x, 0)
const formatResult = (num: number) => `Total: ${num}`

const processArray = compose(formatResult, sum, double, filterNumbers)

const mixedArray = [1, 'hello', 2, 'world', 3, true, 4]
const result = processArray(mixedArray)
// Calculation process: filterNumbers([1,'hello',2,'world',3,true,4]) = [1,2,3,4], double([1,2,3,4]) = [2,4,6,8], sum([2,4,6,8]) = 20, formatResult(20) = 'Total: 20'
console.log(result) // 'Total: 20'

Process Complex Object Transformation

typescript
import { compose } from 'radash'

interface User {
  id: number
  name: string
  email: string
  age: number
}

const getUsers = (data: any) => data.users || []
const filterAdults = (users: User[]) => users.filter(user => user.age >= 18)
const mapNames = (users: User[]) => users.map(user => user.name)
const sortNames = (names: string[]) => names.sort()
const joinNames = (names: string[]) => names.join(', ')

const getAdultUserNames = compose(joinNames, sortNames, mapNames, filterAdults, getUsers)

const data = {
  users: [
    { id: 1, name: 'Alice', email: 'alice@example.com', age: 25 },
    { id: 2, name: 'Bob', email: 'bob@example.com', age: 16 },
    { id: 3, name: 'Charlie', email: 'charlie@example.com', age: 30 },
    { id: 4, name: 'Diana', email: 'diana@example.com', age: 22 }
  ]
}

const result = getAdultUserNames(data)
console.log(result) // 'Alice, Charlie, Diana'

Process Mathematical Calculations

typescript
import { compose } from 'radash'

const add = (a: number) => (b: number) => a + b
const multiply = (a: number) => (b: number) => a * b
const subtract = (a: number) => (b: number) => b - a
const square = (x: number) => x * x

const complexMath = compose(
  square,
  multiply(2),
  add(10),
  subtract(5)
)

const result = complexMath(3)
// Calculation process: subtract(5)(3) = -2, add(10)(-2) = 8, multiply(2)(8) = 16, square(16) = 256
console.log(result) // 256

Notes

  1. Execution order: Functions execute from right to left
  2. Type safety: Ensure function parameter and return value types match
  3. Performance: Composed functions create new functions, be mindful of memory usage
  4. Debugging: Composed functions can be difficult to debug, consider adding logs
  5. Error handling: Ensure intermediate functions can properly handle errors

Differences from Other Methods

  • pipe(): Executes functions from left to right
  • compose(): Executes functions from right to left
  • Manual calls: Requires nested function calls

Practical Application Scenarios

  1. Data processing: Compose multiple data transformation steps
  2. String processing: Compose multiple string operations
  3. Mathematical calculations: Compose multiple mathematical operations
  4. Validation logic: Compose multiple validation steps
  5. Formatting: Compose multiple formatting operations

Released under the MIT License.