@try-error/core
v0.0.1
Published
Lightweight, progressive, type-safe error handling for TypeScript
Maintainers
Readme
try-error
Lightweight, progressive, type-safe error handling for TypeScript
⚠️ Alpha Version: This library is currently in alpha and APIs may change. Not recommended for production use yet.
Why try-error?
Traditional error handling in JavaScript forces you to choose between:
- try/catch blocks: Clunky syntax, no type safety, hidden control flow
- Functional libraries: Heavy abstractions, steep learning curve, large bundle size
try-error provides a middle ground:
- ✅ Errors as values - Explicit error handling without exceptions
- ✅ Zero overhead - Success values are returned directly
- ✅ Type safe - Full TypeScript support with type inference
- ✅ Progressive - Start simple, add complexity as needed
- ✅ Tiny - 4.7KB minified + gzipped (core), 3.1KB (React)
- ✅ Familiar - Looks and feels like JavaScript
Installation
npm install try-error
# or
yarn add try-error
# or
pnpm add try-errorQuick Start
import { trySync, tryAsync, isTryError } from "try-error";
// Wrap synchronous operations
const result = trySync(() => JSON.parse(jsonString));
if (isTryError(result)) {
console.error("Parse failed:", result.message);
} else {
console.log("Parsed:", result); // Type-safe!
}
// Wrap async operations
const data = await tryAsync(async () => {
const response = await fetch("/api/data");
return response.json();
});
if (isTryError(data)) {
console.error("Request failed:", data.message);
} else {
console.log("Data:", data); // Type-safe!
}Core Concepts
1. Errors as Values
Instead of throwing exceptions, operations return either a success value or an error object:
// Returns either User or TryError
const user = trySync(() => validateUser(input));
if (isTryError(user)) {
// Handle error case
console.error(user.message);
} else {
// Use the user (fully typed!)
console.log(user.name);
}2. Rich Error Context
Every error includes debugging information automatically:
const result = trySync(() => JSON.parse("invalid"));
if (isTryError(result)) {
console.log(result.type); // "SyntaxError"
console.log(result.message); // "Unexpected token i in JSON"
console.log(result.source); // "app.ts:42:15"
console.log(result.timestamp); // 1640995200000
console.log(result.stack); // Stack trace (dev only)
}3. Zero Overhead Success Path
Success values are returned directly without wrapping:
const result = trySync(() => 2 + 2);
// result === 4 (not wrapped!)API Overview
Basic Operations
// Synchronous operations
trySync(() => riskyOperation());
trySyncTuple(() => riskyOperation()); // Go-style [value, error]
// Asynchronous operations
await tryAsync(async () => await fetch("/api"));
await tryAsyncTuple(async () => await fetch("/api"));
// Type guards
isTryError(result); // Check if error
isOk(result); // Check if success
isErr(result); // Alias for isTryErrorTransformations
// Transform success values
const upper = tryMap(
trySync(() => getUserName()),
(name) => name.toUpperCase()
);
// Chain operations
const result = tryChain(
trySync(() => parseJSON(input)),
(parsed) => trySync(() => validate(parsed))
);Error Recovery
// Provide defaults
const value = unwrapOr(result, defaultValue);
// Try multiple sources
const data = await tryAnyAsync([
tryAsync(() => fetchFromAPI()),
tryAsync(() => fetchFromCache()),
tryAsync(() => fetchFromDisk()),
]);
// Retry with backoff
const response = await retry(() => tryAsync(() => fetch("/api/flaky")), {
attempts: 3,
baseDelay: 1000,
});Advanced Features
// Add timeout to any operation
const result = await withTimeout(
tryAsync(() => slowOperation()),
5000 // 5 seconds
);
// Process multiple operations
const results = await tryAllAsync([
tryAsync(() => fetch("/api/users")),
tryAsync(() => fetch("/api/posts")),
tryAsync(() => fetch("/api/comments")),
]);React Integration
npm install @try-error/reactimport { useTry } from "@try-error/react";
function UserProfile({ userId }) {
const {
data: user,
error,
isLoading,
execute,
} = useTry(() => fetchUser(userId), { immediate: true, deps: [userId] });
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>Welcome, {user.name}!</div>;
}Comparison with Alternatives
| Feature | try-error | fp-ts | neverthrow | native try/catch | | -------------- | --------- | -------- | ---------- | ---------------- | | Type Safety | ✅ Full | ✅ Full | ✅ Full | ❌ None | | Bundle Size | 4.7KB | ~50KB | ~12KB | 0KB | | Learning Curve | Low | High | Medium | Low | | Zero Overhead | ✅ Yes | ❌ No | ❌ No | ✅ Yes | | Async Support | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | | Error Context | ✅ Rich | ❌ Basic | ❌ Basic | ❌ Basic |
Examples
API Client
class APIClient {
async getUser(id: string) {
return tryAsync(async () => {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
});
}
}
// Usage
const client = new APIClient();
const user = await client.getUser("123");
if (isTryError(user)) {
console.error("Failed to fetch user:", user.message);
} else {
console.log("User:", user.name);
}Form Validation
function validateEmail(email: string) {
return trySync(() => {
if (!email.includes("@")) {
throw new Error("Invalid email format");
}
return email.toLowerCase();
});
}
const result = validateEmail(userInput);
if (isTryError(result)) {
setError(result.message);
} else {
setEmail(result);
}File Processing
async function processFile(path: string) {
// Read file
const content = await tryAsync(() => fs.readFile(path, "utf8"));
if (isTryError(content)) return content;
// Parse JSON
const data = trySync(() => JSON.parse(content));
if (isTryError(data)) return data;
// Validate data
const validated = trySync(() => validateSchema(data));
if (isTryError(validated)) return validated;
return validated;
}Contributing
We welcome contributions! Please see our Contributing Guide for details.
License
MIT © Daniel Johnson
