Skip to content

compose

組合多個函數,創建函數管道。

基礎用法

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)
// 計算過程: square(3) = 9, double(9) = 18, addOne(18) = 19
console.log(result) // 19

語法

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

參數

  • ...fns (function[]): 要組合的函數數組

返回值

返回一個組合後的函數,從右到左執行。

示例

基本函數組合

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')
// 計算過程: repeat('hello') = 'hellohello', toUpperCase('hellohello') = 'HELLOHELLO', addExclamation('HELLOHELLO') = 'HELLOHELLO!'
console.log(result) // 'HELLOHELLO!'

處理數組

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])
// 計算過程: 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

處理對象

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'

處理字符串

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  ')
// 計算過程: 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'

處理數字

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)
// 計算過程: add(5)(2) = 7, multiply(3)(7) = 21, square(21) = 441
console.log(result) // 441

處理類型轉換

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')
// 計算過程: parseNumber('15') = 15, addTen(15) = 25, double(25) = 50, toString(50) = '50'
console.log(result) // '50'

處理驗證和轉換

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)
}

處理異步函數

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)

// 注意:由於fetchUser是異步的,這個組合函數也會返回Promise
const result = await getUserName(1)
console.log(result) // 'ALICE'

處理條件邏輯

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是偶數,double(4)=8, 8*3=24)
console.log(processNumber(5)) // 18 (5是奇數,addOne(5)=6, 6*3=18)

處理錯誤處理

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'

處理管道操作

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)
// 計算過程: 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'

處理復雜對象轉換

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'

處理數學計算

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)
// 計算過程: subtract(5)(3) = -2, add(10)(-2) = 8, multiply(2)(8) = 16, square(16) = 256
console.log(result) // 256

注意事項

  1. 執行順序: 函數從右到左執行
  2. 類型安全: 確保函數參數和返回值類型匹配
  3. 性能: 組合函數會創建新的函數,注意內存使用
  4. 調試: 組合函數可能難以調試,建議添加日志
  5. 錯誤處理: 確保中間函數能正確處理錯誤

與其他方法的區別

  • pipe(): 從左到右執行函數
  • compose(): 從右到左執行函數
  • 手動調用: 需要嵌套函數調用

實際應用場景

  1. 數據處理: 組合多個數據轉換步驟
  2. 字符串處理: 組合多個字符串操作
  3. 數學計算: 組合多個數學運算
  4. 驗證邏輯: 組合多個驗證步驟
  5. 格式化: 組合多個格式化操作

Released under the MIT License.