Skip to content

itsEzz/try-catch

Repository files navigation

@itsezz/try-catch

npm version npm downloads license TypeScript Bundle Size

A lightweight TypeScript utility for elegant error handling using the Result pattern. Say goodbye to messy try/catch blocks and hello to type-safe, composable error management.

Why Result Pattern?

  • Explicit errors — every failure is a deliberate return value
  • Full type safety — TypeScript knows exactly what's possible
  • Composable — chain operations without nesting

Installation

# npm
npm install @itsezz/try-catch

# pnpm
pnpm add @itsezz/try-catch

# yarn
yarn add @itsezz/try-catch

Quick Start

import { tryCatch, isSuccess } from '@itsezz/try-catch';

// Wrap any operation that might fail
const result = tryCatch(() => JSON.parse(userInput));

// Handle both cases explicitly
if (isSuccess(result)) {
  console.log('Parsed data:', result.data); // ✅ Fully typed!
} else {
  console.error('Parse failed:', result.error); // ✅ Error is typed!
}

The Result Type

At the core is the Result<T, E> type — a discriminated union that represents either success or failure:

type Result<T, E = Error> = 
  | { ok: true; data: T; error?: never }   // Success case
  | { ok: false; data?: never; error: E }; // Failure case

The ok property acts as a discriminant, enabling perfect type narrowing.

Usage

Synchronous Functions

import { tryCatch, isSuccess } from '@itsezz/try-catch';

const result = tryCatch(() => {
  const data = fs.readFileSync('config.json', 'utf-8');
  return JSON.parse(data);
});

if (isSuccess(result)) {
  console.log(result.data); // TypeScript knows this is the read config file content
} else {
  console.error(result.error.message);
}

Asynchronous Operations

import { tryCatch, isError } from '@itsezz/try-catch';

const user = await tryCatch(fetch('/api/user').then(r => r.json()));

if (isError(user)) {
  handleError(user.error);
  return;
}

console.log(user.data.name); // Fully typed!

Explicit Context

Need to guarantee sync or async? Use the specific variants:

import { tryCatchSync, tryCatchAsync } from '@itsezz/try-catch';

// Always returns Result<T, E> (never a Promise)
const syncResult = tryCatchSync(() => expensiveCalculation());

// Always returns Promise<Result<T, E>>
const asyncResult = await tryCatchAsync(fetchUser(id));

API Reference

Core Functions

Function Description
tryCatch() Auto-detects sync/async and returns accordingly*
tryCatchSync() Guarantees synchronous Result<T, E>
tryCatchAsync() Guarantees Promise<Result<T, E>>
t() Short alias for tryCatch
tc() Short alias for tryCatchSync
tca() Short alias for tryCatchAsync

* tryCatch may not correctly infer if the result is Promise<Result<T,E>> or Result<T,E> in certain conditions and defaults to Promise<Result<T,E>> when unsure. Use explicit variants for guaranteed type safety.

Type Guards

isSuccess(result) // Returns true if ok === true
isError(result)   // Returns true if ok === false

Transformation

// Transform success data, pass through errors
map(result, (data) => data.toUpperCase())

// Chain another operation that might fail
flatMap(result, (data) => tryCatchSync(() => validate(data)))

Combination

const results = await Promise.all([
  tryCatchAsync(fetchUser(1)),
  tryCatchAsync(fetchUser(2)),
  tryCatchAsync(fetchUser(3)),
]);

// Returns all data if all succeed, or first error if any fail
const allUsers = all(results);

Error Handling

// Pattern matching
match(result, {
  success: (data) => process(data),
  failure: (error) => logError(error),
});

// Get data or default
const value = unwrapOr(result, defaultValue);

// Get data or compute from error
const value = unwrapOrElse(result, (error) => computeFallback(error));

Examples

Custom Error Types

interface ApiError {
  code: number;
  message: string;
}

const result = tryCatchSync<User, ApiError>(() => {
  if (!user) throw { code: 404, message: 'User not found' };
  return user;
});

if (!result.ok) {
  // result.error is typed as ApiError
  console.log(result.error.code); // ✅ No type assertion needed!
}

Functional Pipeline

import { tryCatchAsync, flatMap, map, all } from '@itsezz/try-catch';

const getUserWithPosts = async (userId: string) => {
  const userResult = await tryCatchAsync(fetchUser(userId));
  
  return flatMap(userResult, async (user) => {
    const [postsResult, profileResult] = await all([
      tryCatchAsync(fetchPosts(userId)),
      tryCatchAsync(fetchProfile(userId)),
    ]);
    
    if (!postsResult.ok) return postsResult;
    if (!profileResult.ok) return profileResult;
    
    return { ...user, posts: postsResult.data, profile: profileResult.data };
  });
};

Safe JSON Parsing

import { tryCatch } from '@itsezz/try-catch';

const safeJsonParse = <T>(json: string): T | null => {
  const result = tryCatch(() => JSON.parse(json) as T);
  return result.ok ? result.data : null;
};

// Usage
const config = safeJsonParse<Config>(rawJson);
if (config) {
  // Use config...
}

License

MIT © itsEzz


⭐ If you find this useful, consider starring the repo! ⭐

About

A lightweight TypeScript utility that transforms error handling with type-safe Result patterns, making your code cleaner and more predictable.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors