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.
- ✅ Explicit errors — every failure is a deliberate return value
- ✅ Full type safety — TypeScript knows exactly what's possible
- ✅ Composable — chain operations without nesting
# npm
npm install @itsezz/try-catch
# pnpm
pnpm add @itsezz/try-catch
# yarn
yarn add @itsezz/try-catchimport { 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!
}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 caseThe ok property acts as a discriminant, enabling perfect type narrowing.
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);
}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!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));| 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 |
*
tryCatchmay not correctly infer if the result isPromise<Result<T,E>>orResult<T,E>in certain conditions and defaults toPromise<Result<T,E>>when unsure. Use explicit variants for guaranteed type safety.
isSuccess(result) // Returns true if ok === true
isError(result) // Returns true if ok === false// Transform success data, pass through errors
map(result, (data) => data.toUpperCase())
// Chain another operation that might fail
flatMap(result, (data) => tryCatchSync(() => validate(data)))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);// 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));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!
}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 };
});
};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...
}MIT © itsEzz
⭐ If you find this useful, consider starring the repo! ⭐