A lightweight TypeScript library that elegantly handles operation outcomes through a powerful Result
type, representing either success (Ok
) or failure (Err
).
Inspired by Rust and Kotlin, it brings compile-time error checking and type safety to error handling in TypeScript.
This library eliminates the need for verbose try/catch blocks and null checks by making error handling explicit and type-safe.
You can install the library through npm:
npm install @altevo/okay
import { Ok, Err, type Result } from '@altevo/okay';
// Function that returns a Result
function divide(a: number, b: number): Result<number, string> {
if (b === 0) {
return Err("Division by zero");
}
return Ok(a / b);
}
const result = divide(4, 2);
if (result.isOk) {
// Here we can safely access the value with the proper typing `number`
console.log("Success:", result.value);
} else {
// Here we can safely access the error with the proper typing `string`
console.log("Error:", result.error);
}
You can use various methods provided by the Result
class to handle the result in a more functional way:
const result = divide(4, 0);
// Get the value or null
const valueOrNull = result.getOrNull();
// Get the value or a default value
const valueOrDefault = result.getOrDefault(1);
// Get the value or throw an error
try {
const value = result.getOrThrow();
} catch (error) {
console.error("Caught error:", error);
}
// Transform the ok value with a function
const mappedResult = result.map(value => value * 2);
// Transform the error value with a function
const mappedErrorResult = result.mapError(error => `Error: ${error}`);
// Fold the result into a single value, depending on the state of the result
const foldedValue = result.fold(
value => `Success: ${value}`,
error => `Error: ${error}`
);
// Execute a function if the result is a success
result.onOk(value => console.log("Success:", value));
// Execute a function if the result is an error
result.onError(error => console.error("Error:", error));
You can use the Try
utility to wrap a function that might throw an error. If the function throws an error, it will be caught and returned as an Err
result.
import { Try } from '@altevo/okay';
function doSomethingThatMightThrow(): number {
if (Math.random() < 0.5) {
return 42;
} else {
throw new Error("Something went wrong");
}
}
// Here tryResult is Result<number, Error>
const tryResult = Try(doSomethingThatMightThrow)
The same principle apply to asynchronous functions. You can use the TryAsync
utility to wrap an asynchronous function that might throw an error.
import { TryAsync } from '@altevo/okay';
async function fetchData(): Promise<string> {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
return await response.text();
}
const asyncResult = await TryAsync(fetchData);
// After await, asyncResult would be Result<string, Error>
result.fold(
value => console.log("Fetched data:", value),
error => console.error("Failed to fetch data:", error)
);
Here are the main types and functions provided by the library
-
Result<T, E>
: A type that represents either a successful value (Ok<T>
) or an error (Err<E>
). A result is eitherOk
orErr
, never both and its immutable. -
AsyncResult<T, E>
: A type that represents a promise of aResult<T, E>
. (It is an alias ofPromise<Result<T, E>>
)
-
Ok<T>(val: T): OkResult<T>
: Creates a successful result. -
Err<E>(val: E): ErrResult<E>
: Creates an error result. -
Try<T>(fn: () => T): Result<T, Error>
: Executes a function and returns a result with the function's return value. If the inner function throws an error, it returns anErr
result. -
TryAsync<T>(fn: () => Promise<T>): AsyncResult<T, Error>
: Same asTry
, but for asynchronous functions, returning a promise. The inner function will be awaited, and the result will be wrapped in a promise.
Note
All methods of the Result
class are documented as JSDoc comments in the source code.
This library was inspired by the native Result
implementation in Rust and Kotlin, and it aims to provide a similar experience for TypeScript projects.
The implementation was deeply influenced by the ts-results
library, but it was rewritten from scratch to provide a simpler and friendlier API, similar to Kotlin's Result
class.
This project is licensed under the MIT License - see the LICENSE file for details.