@itsezz/try-catch
Version:
A TypeScript utility for elegant error handling with Result types
241 lines (171 loc) • 6.66 kB
Markdown
# @itsezz/try-catch
<div align="center">
[](https://www.npmjs.com/package/@itsezz/try-catch)
[](https://www.npmjs.com/package/@itsezz/try-catch)
[](https://github.com/itsEzz/try-catch/blob/main/LICENSE)
[](https://www.typescriptlang.org/)
[](https://bundlephobia.com/package/@itsezz/try-catch)
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.
</div>
## 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
```bash
# npm
npm install @itsezz/try-catch
# pnpm
pnpm add @itsezz/try-catch
# yarn
yarn add @itsezz/try-catch
```
## Quick Start
```typescript
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:
```typescript
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
```typescript
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
```typescript
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:
```typescript
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
```typescript
isSuccess(result) // Returns true if ok === true
isError(result) // Returns true if ok === false
```
### Transformation
```typescript
// Transform success data, pass through errors
map(result, (data) => data.toUpperCase())
// Chain another operation that might fail
flatMap(result, (data) => tryCatchSync(() => validate(data)))
```
### Combination
```typescript
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
```typescript
// 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
```typescript
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
```typescript
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
```typescript
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](https://github.com/itsEzz)
---
<div align="center">
**⭐ If you find this useful, consider starring the repo! ⭐**
</div>