try-error Documentation

TryResult vs Exceptions

A detailed comparison between try-error and traditional exception handling

Quick Comparison

Aspecttry-errorExceptions
Type Safety✅ Errors visible in types❌ Invisible to type system
Performance✅ Zero overhead success path❌ Stack unwinding cost
Explicit Handling✅ Must check before use❌ Easy to forget catch
Control Flow✅ Predictable returns❌ Unpredictable jumps
Learning Curve⚠️ New patterns to learn✅ Familiar to most devs
Ecosystem⚠️ Newer approach✅ Universal support

Detailed Comparisons

Type Safety

❌ Exceptions

Exception-based Approachtypescript
// No indication of possible errors
function parseJSON(input: string): object {
  return JSON.parse(input); // Might throw!
}

// Caller has no type-level guidance
const result = parseJSON(userInput);
// What if it throws? TypeScript can't help

✅ try-error

try-error Approachtypescript
// Clear error possibility in return type
function parseJSON(input: string): TryResult<object, TryError> {
  return trySync(() => JSON.parse(input));
}

// Caller must handle both cases
const result = parseJSON(userInput);
if (isTryError(result)) {
  // Handle error case
} else {
  // Use successful result
}

Performance

Performance Impact: Exception throwing involves stack unwinding, which is expensive. try-error has zero overhead for success cases and minimal overhead for errors.

Performance Comparisontypescript
1// Exception performance cost
2try {
3  const result = riskyOperation(); // If this throws...
4  return result;
5} catch (error) {
6  // Stack unwinding happened here (expensive)
7  return null;
8}
9
10// try-error performance
11const result = trySync(() => riskyOperation());
12if (isTryError(result)) {
13  // No stack unwinding, just a return value
14  return null;
15}
16return result; // Zero overhead for success

Error Propagation

Exceptions

Exception Propagationtypescript
async function processData() {
  try {
    const step1 = await fetchData();
    const step2 = await processStep1(step1);
    const step3 = await processStep2(step2);
    return step3;
  } catch (error) {
    // Which step failed? Hard to tell
    console.error('Something failed:', error);
    throw error;
  }
}

try-error

try-error Propagationtypescript
async function processData() {
  const step1 = await tryAsync(() => fetchData());
  if (isTryError(step1)) return step1;
  
  const step2 = await tryAsync(() => processStep1(step1));
  if (isTryError(step2)) return step2;
  
  const step3 = await tryAsync(() => processStep2(step2));
  return step3; // Clear which step succeeded/failed
}

When to Use Each Approach

✅ Use try-error for:

  • • New code where you control the API
  • • Operations that commonly fail (network, parsing, validation)
  • • When you want explicit error handling
  • • Performance-critical code
  • • When type safety is important
  • • Complex error handling logic

✅ Use exceptions for:

  • • Integrating with existing exception-based APIs
  • • Truly exceptional conditions
  • • When you need to bubble up through many layers
  • • Library code that needs to match ecosystem patterns
  • • Programming errors (assertions)
  • • Legacy codebases where consistency matters

Hybrid Approaches

You don't have to choose one or the other. Here are strategies for using both:

Boundary Pattern

Boundary Patterntypescript
1// Use try-error internally, exceptions at boundaries
2class UserService {
3  // Internal methods use try-error
4  private async fetchUserData(id: string): Promise<TryResult<User, TryError>> {
5    const result = await tryAsync(() => this.api.getUser(id));
6    return result;
7  }
8  
9  // Public API uses exceptions for compatibility
10  async getUser(id: string): Promise<User> {
11    const result = await this.fetchUserData(id);
12    if (isTryError(result)) {
13      throw new Error(`Failed to fetch user: ${result.message}`);
14    }
15    return result;
16  }
17}

Adapter Pattern

Adapter Patterntypescript
// Wrap exception-based APIs with try-error
function safeFetch(url: string): Promise<TryResult<Response, TryError>> {
  return tryAsync(() => fetch(url));
}

// Convert try-error to exceptions when needed
function throwingParse(json: string): object {
  const result = trySync(() => JSON.parse(json));
  if (isTryError(result)) {
    throw new Error(result.message);
  }
  return result;
}

Performance Considerations

Benchmark Results: In typical scenarios, try-error shows 2-10x better performance for error cases and identical performance for success cases compared to try/catch.

Performance Benchmark Exampletypescript
1// Performance comparison example
2// Exception version: ~100ms for 1000 errors
3function parseWithExceptions(inputs: string[]) {
4  const results = [];
5  for (const input of inputs) {
6    try {
7      results.push(JSON.parse(input));
8    } catch {
9      results.push(null);
10    }
11  }
12  return results;
13}
14
15// try-error version: ~20ms for 1000 errors
16function parseWithTryError(inputs: string[]) {
17  const results = [];
18  for (const input of inputs) {
19    const result = trySync(() => JSON.parse(input));
20    results.push(isTryError(result) ? null : result);
21  }
22  return results;
23}

Next Steps

Error Types

Learn about TryError structure and custom error types

Explore Error Types →

Migration Guide

Step-by-step guide to adopting try-error

Start Migration →

API Reference

Complete API documentation and examples

View API Docs →