tryError Documentation

Migration Guides

Step-by-step guides for migrating from other error handling approaches to tryError

Migrating from try/catch

The most common migration scenario - moving from traditional try/catch blocks to tryError.

Before (try/catch)

Traditional try/catch Approachtypescript
1async function fetchUser(id: string): Promise<User | null> {
2  try {
3    const response = await fetch(`/api/users/${id}`);
4    if (!response.ok) {
5      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
6    }
7    return await response.json();
8  } catch (error) {
9    console.error('Failed to fetch user:', error);
10    return null; // Lost error information
11  }
12}
13
14// Usage
15const user = await fetchUser('123');
16if (!user) {
17  // Can't tell if user doesn't exist or if there was an error
18  console.log('No user found or error occurred');
19}

After (tryError)

tryError Approachtypescript
1import { tryAsync, isTryError } from '@try-error/core';
2
3async function fetchUser(id: string): Promise<TryResult<User, TryError>> {
4  return tryAsync(async () => {
5    const response = await fetch(`/api/users/${id}`);
6    if (!response.ok) {
7      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
8    }
9    return await response.json();
10  });
11}
12
13// Usage
14const result = await fetchUser('123');
15if (isTryError(result)) {
16  console.error('Failed to fetch user:', result.message);
17  // Full error context available
18  console.log('Error details:', result.context);
19} else {
20  console.log('User found:', result.name);
21}

Migration Benefits

  • • Explicit error handling - no silent failures
  • • Rich error context preserved
  • • Type-safe error handling
  • • Better debugging information
  • • Consistent error patterns across codebase

Migrating from neverthrow

If you're coming from neverthrow or similar Result libraries, tryError offers a more JavaScript-native approach.

Before (neverthrow)

neverthrow Result Patterntypescript
1import { Result, ok, err } from 'neverthrow';
2
3async function fetchUser(id: string): Promise<Result<User, Error>> {
4  try {
5    const response = await fetch(`/api/users/${id}`);
6    if (!response.ok) {
7      return err(new Error(`HTTP ${response.status}`));
8    }
9    const user = await response.json();
10    return ok(user);
11  } catch (error) {
12    return err(error as Error);
13  }
14}
15
16// Usage - requires learning Result API
17const result = await fetchUser('123');
18result.match(
19  (user) => console.log('User:', user),
20  (error) => console.error('Error:', error.message)
21);

After (tryError)

tryError Equivalenttypescript
1import { tryAsync, isTryError } from '@try-error/core';
2
3async function fetchUser(id: string): Promise<TryResult<User, TryError>> {
4  return tryAsync(async () => {
5    const response = await fetch(`/api/users/${id}`);
6    if (!response.ok) {
7      throw new Error(`HTTP ${response.status}`);
8    }
9    return await response.json();
10  });
11}
12
13// Usage - familiar JavaScript patterns
14const result = await fetchUser('123');
15if (isTryError(result)) {
16  console.error('Error:', result.message);
17} else {
18  console.log('User:', result);
19}

Key Differences

  • • No need to learn monadic patterns (map, flatMap, etc.)
  • • Uses familiar JavaScript error throwing
  • • Simpler type system - just union types
  • • Better integration with existing JavaScript code
  • • Rich error context out of the box

Migrating from fp-ts Either

Moving from fp-ts Either to tryError for teams wanting functional error handling without the complexity.

Before (fp-ts)

fp-ts Either Patterntypescript
1import { Either, left, right, fold } from 'fp-ts/Either';
2import { pipe } from 'fp-ts/function';
3import { TaskEither, tryCatch } from 'fp-ts/TaskEither';
4
5const fetchUser = (id: string): TaskEither<Error, User> =>
6  tryCatch(
7    () => fetch(`/api/users/${id}`).then(r => r.json()),
8    (reason) => new Error(String(reason))
9  );
10
11// Usage - requires fp-ts knowledge
12pipe(
13  await fetchUser('123')(),
14  fold(
15    (error) => console.error('Error:', error.message),
16    (user) => console.log('User:', user)
17  )
18);

After (tryError)

tryError Equivalenttypescript
import { tryAsync, isTryError } from '@try-error/core';

const fetchUser = (id: string) =>
  tryAsync(() => fetch(`/api/users/${id}`).then(r => r.json()));

// Usage - straightforward JavaScript
const result = await fetchUser('123');
if (isTryError(result)) {
  console.error('Error:', result.message);
} else {
  console.log('User:', result);
}

Step-by-Step Migration Process

1. Install tryError

Installation
$pnpm add @try-error/core

2. Identify Migration Candidates

Look for these patterns in your codebase:

  • • Functions that return null/undefined on error
  • • try/catch blocks that swallow errors
  • • Inconsistent error handling patterns
  • • Functions that throw but callers don't handle errors

3. Start with Leaf Functions

Begin migration with functions that don't call other functions - typically API calls, file operations, or parsing functions.

4. Update Function Signatures

Function Signature Updatestypescript
// Before
function parseJson(str: string): any | null

// After  
function parseJson(str: string): TryResult<any, TryError>

5. Update Callers Gradually

Work your way up the call stack, updating callers to handle the new return types. You can mix tryError with existing patterns during migration.

6. Add Error Context

Enhance your error handling by adding context information to help with debugging.

Common Migration Patterns

Pattern 1: Null Returns → TryResult

Before

Null Return Patterntypescript
function findUser(id: string): User | null {
  try {
    return database.findById(id);
  } catch {
    return null;
  }
}

After

TryResult Patterntypescript
function findUser(id: string): TryResult<User, TryError> {
  return trySync(() => database.findById(id));
}

Pattern 2: Boolean Success → TryResult

Before

Boolean Success Patterntypescript
function saveUser(user: User): boolean {
  try {
    database.save(user);
    return true;
  } catch {
    return false;
  }
}

After

TryResult Patterntypescript
function saveUser(user: User): TryResult<void, TryError> {
  return trySync(() => database.save(user));
}

Migration Checklist

Related Pages

Quick Start

Get up and running with tryError quickly

View Quick Start →

Best Practices

Learn the recommended patterns and practices

View Best Practices →