Skip to main content

@kreisler/try-catch

Functional utilities for error handling that eliminate the need for verbose try-catch blocks. Provides composable, type-safe error handling patterns.

Installation

npm install @kreisler/try-catch

Functions

tryCatch

Synchronous error handling that returns a tuple with error and result.
fn
(...args: P) => D
required
The function to execute. Can be synchronous.
...args
P
Arguments to pass to the function.
return
[null, D] | [Error]
Returns a tuple where:
  • On success: [null, result] - error is null, result is the function return value
  • On error: [Error] - contains the caught error

tryCatchPromise

Asynchronous error handling for promises that returns a tuple with error and result.
fn
(...args: P) => Promise<D> | D
required
The function to execute. Can be asynchronous or synchronous.
...args
P
Arguments to pass to the function.
return
Promise<[null, D] | [Error]>
Returns a promise that resolves to a tuple where:
  • On success: [null, result] - error is null, result is the function return value
  • On error: [Error] - contains the caught error

tryToCatch

Executes a function and catches errors, allowing recovery with an alternative value.
tryFunction
() => TTryResult
required
The function to attempt execution.
catchFunction
(ex: unknown) => TCatchResult
required
Function to execute if an error occurs. Receives the error and can return a fallback value.
return
TTryResult | TCatchResult
Returns either the result of tryFunction on success or the result of catchFunction on error.

tryFinally

Executes a function with guaranteed cleanup via a finally block.
tryFunction
() => TTryResult
required
The function to execute.
finallyFunction
() => void
required
Function to execute in the finally block, regardless of success or failure.
return
TTryResult | undefined
Returns the result of tryFunction if successful, or undefined if an error occurred.

tryCatchFinally

Executes a function with error recovery and guaranteed cleanup.
tryFunction
() => TTryResult
required
The function to attempt execution.
catchFunction
(ex: unknown) => TCatchResult
required
Function to execute if an error occurs.
finallyFunction
() => void
required
Function to execute in the finally block.
return
TTryResult | TCatchResult
Returns either the result of tryFunction or catchFunction.

Type Definitions

type TtryCatchPromise = Promise<TtryCatch>
type TtryCatch = [error: Error | null | unknown, result?: any]
type Tfn = (...args: any[]) => any
type T = any

Usage Examples

tryCatch - Basic Usage

import { tryCatch } from '@kreisler/try-catch'

const [error, result] = tryCatch(() => {
  return JSON.parse('{"valid": "json"}')
})

if (error) {
  console.error('Parse failed:', error.message)
} else {
  console.log('Parsed data:', result)
}

tryCatch - With Error

import { tryCatch } from '@kreisler/try-catch'

const [error, result] = tryCatch(() => {
  throw new Error('Something went wrong')
})

if (error) {
  console.error(error.message) // "Something went wrong"
} else {
  console.log(result) // Won't execute
}

tryCatchPromise - Fetch API

import { tryCatchPromise } from '@kreisler/try-catch'

const [error, result] = await tryCatchPromise(
  globalThis.fetch,
  'https://jsonplaceholder.typicode.com/todos/1'
)

if (error) {
  console.error('Fetch failed:', error.message)
} else {
  const data = await result.json()
  console.log(data)
}

tryCatchPromise - Async Function

import { tryCatchPromise } from '@kreisler/try-catch'

const fetchUser = async (id: number) => {
  const response = await fetch(`/api/users/${id}`)
  if (!response.ok) throw new Error('User not found')
  return response.json()
}

const [error, user] = await tryCatchPromise(fetchUser, 123)

if (error) {
  console.error('Failed to fetch user:', error.message)
} else {
  console.log('User:', user)
}

tryToCatch - Fallback Value

import { tryToCatch } from '@kreisler/try-catch'

const result = tryToCatch(
  () => {
    return JSON.parse(invalidJson)
  },
  (error) => {
    console.error('Parse failed, using default')
    return { default: true }
  }
)

console.log(result) // { default: true }

tryToCatch - Error Recovery

import { tryToCatch } from '@kreisler/try-catch'

const getUserName = (userId: number) => {
  return tryToCatch(
    () => {
      const user = database.getUser(userId)
      if (!user) throw new Error('User not found')
      return user.name
    },
    (error) => {
      return 'Anonymous'
    }
  )
}

console.log(getUserName(999)) // "Anonymous"

tryFinally - Resource Cleanup

import { tryFinally } from '@kreisler/try-catch'

const result = tryFinally(
  () => {
    console.log('Performing operation...')
    return 'Success'
  },
  () => {
    console.log('Cleanup complete')
  }
)

// Logs:
// "Performing operation..."
// "Cleanup complete"
// Returns: "Success"

tryCatchFinally - Complete Error Handling

import { tryCatchFinally } from '@kreisler/try-catch'

const result = tryCatchFinally(
  () => {
    console.log('Attempting operation...')
    throw new Error('Operation failed')
  },
  (ex) => {
    console.error('Error caught:', ex.message)
    return 'Fallback value'
  },
  () => {
    console.log('Cleanup executed')
  }
)

// Logs:
// "Attempting operation..."
// "Error caught: Operation failed"
// "Cleanup executed"
// Returns: "Fallback value"

File Processing Example

import { tryCatchFinally } from '@kreisler/try-catch'
import * as fs from 'fs'

const processFile = (filename: string) => {
  let fileHandle: number | null = null

  return tryCatchFinally(
    () => {
      fileHandle = fs.openSync(filename, 'r')
      const content = fs.readFileSync(fileHandle, 'utf-8')
      return JSON.parse(content)
    },
    (error) => {
      console.error('Failed to process file:', error)
      return null
    },
    () => {
      if (fileHandle !== null) {
        fs.closeSync(fileHandle)
        console.log('File handle closed')
      }
    }
  )
}

API Call with Tuple Pattern

import { tryCatchPromise } from '@kreisler/try-catch'

const apiCall = async (endpoint: string) => {
  const [error, response] = await tryCatchPromise(fetch, endpoint)
  
  if (error) {
    return [error, null] as const
  }

  const [parseError, data] = await tryCatchPromise(() => response.json())
  
  if (parseError) {
    return [parseError, null] as const
  }

  return [null, data] as const
}

// Usage
const [error, data] = await apiCall('/api/users')

if (error) {
  console.error('API call failed:', error.message)
} else {
  console.log('Users:', data)
}

Database Transaction

import { tryCatchFinally } from '@kreisler/try-catch'

const updateUserWithTransaction = (userId: number, updates: object) => {
  return tryCatchFinally(
    () => {
      db.beginTransaction()
      const user = db.users.update(userId, updates)
      db.commit()
      return user
    },
    (error) => {
      console.error('Transaction failed:', error)
      db.rollback()
      throw error
    },
    () => {
      db.closeConnection()
    }
  )
}

Comparison with Traditional Try-Catch

Traditional Approach

let result
try {
  result = await fetchData()
} catch (error) {
  console.error(error)
  result = null
}

With @kreisler/try-catch

const [error, result] = await tryCatchPromise(fetchData)
if (error) console.error(error)

Benefits

  • Cleaner Code: Eliminates nested try-catch blocks
  • Type Safety: Full TypeScript support with proper type inference
  • Composability: Easy to chain and combine error handling patterns
  • Explicit Error Handling: Forces you to handle errors explicitly
  • Functional Style: Aligns with functional programming principles
  • Tuple Pattern: Go-style error handling for JavaScript/TypeScript

Build docs developers (and LLMs) love