Safe-ish
Safe-ish error handling.
A lightweight TypeScript library for robust error handling using a Result-like pattern. It helps you safely execute functions, catch exceptions gracefully, and work with predictable success (SafeishResult) and error (SafeishError) types, avoiding runtime crashes from unhandled exceptions.
Why safeish?
- Prevent Crashes: Wrap functions with
safeish to automatically catch thrown exceptions and convert them into structured SafeishError objects.
- Explicit Error Handling: Functions return either a success (
SafeishResult) or an error (SafeishError), making potential failure points explicit in the type system.
- Structured Errors: Define consistent error types with unique codes, context-aware messages, and optional side effects (like logging) using
defineError.
- Reduce Boilerplate: Avoid repetitive
try...catch blocks for expected and unexpected errors.
Installation
With npm:
npm install safeish
With any other package manager:
pnpm add safeish
yarn add safeish
bun add safeish
npx jsr add @frantss/safeish
Usage
import { safeish, safeish_defineError, safeish_result } from 'safeish/prefixed';
const errors = {
greater_than_50: safeish_defineError({
code: 'greater_than_50',
message: (context: number) => `Value ${context} is greater than 50`,
}),
greater_than_65: safeish_defineError({
code: 'greater_than_65',
message: (context: number) => `Value ${context} is greater than 65`,
}),
greater_than_80: safeish_defineError({
code: 'greater_than_80',
message: () => 'Value is greater than 80',
effect: () => {
console.log('effect');
},
}),
};
errors.greater_than_50.code;
errors.greater_than_50.message;
errors.greater_than_50.$context;
const someFunctionThatMightFail = safeish(() => {
const value = Math.random();
if (value > 0.5) return errors.greater_than_50(value);
if (value > 0.8) return errors.greater_than_80();
return safeish_result(value);
});
const result = someFunctionThatMightFail();
if (result.ok) {
result.data;
} else {
result.error.code;
result.error.message;
if (result.error.code === 'greater_than_50') {
result.error.context;
} else if (result.error.code === 'greater_than_80') {
result.error.context;
} else {
result.error.code;
result.error.context;
}
}
With ts-pattern
Safe-ish works great when paired with ts-pattern, a pattern matching library for TypeScript.
It allows you to easily ensure you are handling all possible errors exhaustively.
const result = someFunctionThatMightFail();
match(result)
.with({ ok: true }, (data) => {
})
.with({ error: { code: 'greater_than_50' } }, (error) => {
error.error.context;
})
.with({ error: { code: 'greater_than_80' } }, (error) => {
error.error.context;
})
.with({ error: { code: '~unhandled' } }, (error) => {
error.error.context;
})
.exhaustive();
API
defineError
Defines a reusable blueprint for a specific error type, including its unique code, context-aware message generation function, and optional side effect function (e.g., for logging).
result
Creates a SafeishResult object representing a successful outcome ("Ok"), wrapping the success payload.
error
Creates an ad-hoc SafeishError object representing a failure ("Err"). Prefer using the factory function returned by defineError for consistency and to automatically trigger side effects.
safeish
Wraps a function to provide safe execution. It catches any exceptions thrown by the wrapped function and maps them to a specified error definition, ensuring the function always returns a Promise resolving to a SafeishResult or SafeishError.
Inspirations
This library was inspired by the following projects: