lib-safe-promise
v0.1.0
Published
Type-safe utilities for working with promises.
Readme
lib-safe-promise
SafePromise is a small TypeScript utility for representing failures as typed Error values when you await a promise. It is useful when a caller should handle an expected failure explicitly instead of relying on try/catch.
Install
pnpm add lib-safe-promiseUsage
Wrap a promise with SafePromise.from and provide a function that converts an unknown rejection into your application error type.
import { SafePromise } from "lib-safe-promise";
class LoadUserError extends Error {}
type User = { id: string; name: string };
const user = await SafePromise.from(
fetch("/api/users/42").then((response) => response.json() as Promise<User>),
(error) => new LoadUserError(`Could not load user: ${error.message}`),
);
// user is User | LoadUserError
if (user instanceof LoadUserError) {
console.error(user.message);
} else {
console.log(user.name);
}The formatter always receives an Error. A rejection such as a string or an object is first converted with new Error(String(reason)).
API
SafePromise.from(promise, formatError)
Wraps a native promise. On success, awaiting the result returns its value. If the wrapped promise rejects, awaiting the result returns formatError(error) instead. The resulting type is TResult | TError.
SafePromise.resolve(value)
Creates an already-resolved SafePromise. This is handy when a function returns a SafePromise on every branch.
const answer = await SafePromise.resolve(42); // numberAn Error supplied as a resolved value remains a value; it is not treated as a rejection.
SafePromise.try(fn, formatError)
Runs a function through Promise.try. Synchronous throws (and rejections from a promise returned by fn) are formatted just like rejections passed to from.
const result = await SafePromise.try(
() => JSON.parse(input),
(error) => new SyntaxError(`Invalid JSON: ${error.message}`),
);.unsafe()
Returns a normal Promise with Error removed from its resolved type. It rejects if the resolved value is an Error.
const result = await SafePromise.resolve<number | Error>(42).unsafe();
// result is numberunsafe() operates on the underlying promise directly: if a promise passed to from rejects, that rejection is propagated unchanged rather than being formatted. Use await safePromise first when you want formatted failures.
Notes
SafePromise is an awaitable thenable, not a subclass of Promise. It only converts rejections of the promise it wraps; errors thrown later in your own callbacks still behave as normal promise rejections.
