bakutils-catcher
v5.0.0
Published
Utility library for handling errors elegantly with Decorators and Rust-like algebraic types.
Downloads
951
Maintainers
Readme
bakutils-catcher
bakutils-catcher
is a lightweight, easy-to-use TypeScript library providing utilities for robust error handling and functional programming patterns inspired by Rust. It includes:
- Decorators: For catching exceptions and errors in your code using TypeScript decorators.
- Algebraic Data Types: Implementations of
Result
,Option
, andOneOf
types for expressive and safe error handling. - Utilities: Helper functions and types to support the main features.
Table of Content
Installation
Install the package via npm, yarn, or pnpm:
# npm
npm install bakutils-catcher
# yarn
yarn add bakutils-catcher
# pnpm
pnpm add bakutils-catcher
Features
Algebraic Data Types
Result
The Result
type represents either success (Ok
) or failure (Err
) of an operation, providing expressive error handling without exceptions.
Ok
: Represents a successful computation.- Methods:
unwrap()
: Returns the contained value.unwrapOr(defaultValue)
: Returns the contained value.unwrapOrElse(fn)
: Returns the contained value.isOk()
: Returnstrue
.isErr()
: Returnsfalse
.map(fn)
: Applies a function to the contained value, returning a newResult
.flatMap(fn)
: Applies a function that returns aResult
, flattening the result.toOption()
: ConvertsOk
toSome
.match(handlers)
: Pattern matches onOk
.
- Methods:
Err
: Represents a failed computation.- Methods:
unwrap()
: Throws the contained error.unwrapOr(defaultValue)
: ReturnsdefaultValue
.unwrapOrElse(fn)
: Callsfn
with the error and returns its result.isOk()
: Returnsfalse
.isErr()
: Returnstrue
.map(fn)
: ReturnsErr
without applyingfn
.flatMap(fn)
: ReturnsErr
without applyingfn
.toOption()
: ConvertsErr
toNone
.match(handlers)
: Pattern matches onErr
.
- Methods:
Usage Example:
import { Ok, Err, Result } from 'bakutils-catcher';
function parseJSON(jsonString: string): Result<any, Error> {
try {
const data = JSON.parse(jsonString);
return Ok(data);
} catch (error) {
return Err(error);
}
}
const result = parseJSON('{"valid": "json"}');
result.match({
Ok: (data) => console.log('Parsed data:', data),
Err: (error) => console.error('Parsing failed:', error),
});
Option
The Option
type represents an optional value: every Option
is either Some
and contains a value, or None
, and does not.
Some
: Represents anOption
that contains a value.- Methods:
unwrap()
: Returns the contained value.unwrapOr(defaultValue)
: Returns the contained value.unwrapOrElse(fn)
: Returns the contained value.isSome()
: Returnstrue
.isNone()
: Returnsfalse
.map(fn)
: Applies a function to the contained value, returning a newOption
.flatMap(fn)
: Applies a function that returns anOption
, flattening the result.okOr(err)
: ConvertsSome
toOk
.match(handlers)
: Pattern matches onSome
.
- Methods:
None
: Represents anOption
with no value.- Methods:
unwrap()
: Throws an error.unwrapOr(defaultValue)
: ReturnsdefaultValue
.unwrapOrElse(fn)
: Callsfn
and returns its result.isSome()
: Returnsfalse
.isNone()
: Returnstrue
.map(fn)
: ReturnsNone
without applyingfn
.flatMap(fn)
: ReturnsNone
without applyingfn
.okOr(err)
: ConvertsNone
toErr
.match(handlers)
: Pattern matches onNone
.
- Methods:
Usage Example:
import { Option, Some, None } from 'bakutils-catcher';
function getConfigValue(key: string): Option<string> {
const value = process.env[key];
return value ? Some(value) : None;
}
const configValue = getConfigValue('API_KEY');
if (configValue.isSome()) {
console.log('API Key:', configValue.unwrap());
} else {
console.error('API Key is not set');
}
OneOf
The OneOf
type represents a value that can be one of several possible types, each identified by a label. It's similar to a tagged union or variant type.
Usage Example:
import { createOneOf, OneOf } from 'bakutils-catcher';
// Define labels and corresponding types
type Shape = {
Circle: { radius: number };
Square: { side: number };
Rectangle: { width: number; height: number };
};
// Create instances of OneOf
const circle: OneOf<Shape> = createOneOf('Circle', { radius: 5 });
const square: OneOf<Shape> = createOneOf('Square', { side: 10 });
// Use the `match` method to handle each shape
function area(shape: OneOf<Shape>): number {
return shape.match({
Circle: ({ radius }) => Math.PI * radius ** 2,
Square: ({ side }) => side ** 2,
Rectangle: ({ width, height }) => width * height,
});
}
console.log('Circle area:', area(circle)); // Circle area: 78.53981633974483
console.log('Square area:', area(square)); // Square area: 100
Methods:
match(handlers)
: Matches the variant with the corresponding handler function.is(label)
: Type guard to check if the variant matches the specified label, refining the type ofvalue
.
Decorators
Decorators provide a way to add annotations and a meta-programming syntax for class declarations and members.
Note: When using decorators, make sure your tsconfig.json
file has the experimentalDecorators
and emitDecoratorMetadata
properties enabled. Additionally, if a handler is not configured, you can install the reflect-metadata
library, as it works for decorators but is not included in this package to keep it dependency-free.
Update: In version ^5.0.0, the Catch
decorator was renamed to Catcher
, and DefaultCatch
was renamed to DefaultCatcher
to maintain consistency with the function names.
@Catcher
A decorator that wraps a class method with error-handling logic, catching errors of a specific type thrown within the method.
Usage:
import { Catcher } from 'bakutils-catcher';
class FileReader {
@Catcher(FileNotFoundError, (err, context, ...args) => {
console.error('File not found:', err);
return null; // Provide a fallback value
})
readFile(path: string): string | null {
// Implementation that might throw FileNotFoundError
}
}
@DefaultCatcher
A decorator that wraps a class method with error-handling logic, catching all errors thrown within the method.
Usage:
import { DefaultCatcher } from 'bakutils-catcher';
class DataService {
@DefaultCatcher((err, context, ...args) => {
console.error('An error occurred:', err);
// Handle the error, possibly returning a fallback value
})
fetchData(url: string): Data {
// Implementation that might throw errors
}
}
catcher
function
The catcher
function allows you to wrap any function with error-handling logic, catching errors of a specific type. This is useful when you cannot use decorators or prefer a functional approach.
Usage:
import { catcher } from 'bakutils-catcher';
class CustomError extends Error {
constructor(message: string) {
super(message);
this.name = "CustomError";
}
}
function riskyOperation(value: number): string {
if (value < 0) throw new CustomError("Negative value not allowed");
return value.toFixed(2);
}
const safeOperation = catcher(
riskyOperation,
CustomError,
(err, context, value) => {
console.error("Caught CustomError:", err.message);
return "Fallback Value";
}
);
console.log(safeOperation(-1)); // Output: "Fallback Value"
console.log(safeOperation(10)); // Output: "10.00"
defaultCatcher
function
The defaultCatcher
function allows you to wrap any function with error-handling logic, catching all errors. This is similar to catcher
but does not require specifying an error class.
Usage:
import { defaultCatcher } from 'bakutils-catcher';
function anotherRiskyOperation(value: string): number {
if (isNaN(Number(value))) throw new Error("Invalid number");
return Number(value);
}
const safeAnotherOperation = defaultCatcher(
anotherRiskyOperation,
(err, context, value) => {
console.error("Caught an error:", err.message);
return 0;
}
);
console.log(safeAnotherOperation("abc")); // Output: 0
console.log(safeAnotherOperation("123")); // Output: 123
Quick Start
Here's a quick example demonstrating how to use the main features of the library:
import {
Ok,
Err,
Option,
Some,
None,
createOneOf,
OneOf,
Catcher,
DefaultCatch,
} from 'bakutils-catcher';
// Using Result
function calculate(a: number, b: number): Result<number, string> {
if (b === 0) {
return Err('Cannot divide by zero');
}
return Ok(a / b);
}
// Using Option
const someValue = Some(42);
const noValue = None;
// Using OneOf
type Response = {
Success: { data: any };
Error: { message: string };
};
const response: OneOf<Response> = createOneOf('Error', { message: 'Not Found' });
response.match({
Success: ({ data }) => console.log('Data:', data),
Error: ({ message }) => console.error('Error:', message),
});
// Using Decorators
class ApiService {
@Catcher(NetworkError, (err, context, ...args) => {
console.error('Network error:', err);
return null; // Fallback value
})
fetchData(endpoint: string): Data | null {
// Implementation that might throw NetworkError
}
@DefaultCatch((err, context, ...args) => {
console.error('Unhandled error:', err);
// Handle error
})
processData(data: Data): void {
// Implementation that might throw errors
}
}
License
This project is licensed under the MIT License.