Better error handling stolen from rust. Return result values instead of throwing exceptions. Handle every error result with minimum nesting.
npm i base-ts-result
yarn add base-ts-result
pnpm add base-ts-result
import { Ok, Err, type Result } from 'base-ts-result';
const gonnaThrow = () => {
throw new Error("you have to catch me or i'll crash the app");
};
const res = toResult(gonnaThrow);
res.isErr() // true
/**
* @description returns random value, returns error if the value <= 0.5
*/
const getRes = (): Result<number, string> => {
const val = Math.random();
if (val > 0.5) {
return Ok(val);
}
return Err('number is too low');
};
/**
* @description returns random value or zero if random value <= 0.5
*/
const handleRes = () => {
return getRes()
.inspect(v => console.log(`Got ok value ${v}`))
.inspectErr(e => console.error(e))
.unwrapOr(0);
};
const val = handleRes();
console.log(val);
Handy functions to create Result objects
import { Ok, Err } from 'base-ts-result';
// create success Result
const okRes = Ok('success');
// create error Result
const errRes = Err('failure');
Interface that contains operation result and interaction methods
Unwraps result value or throws an error if the result is Err.
import { Ok, Err } from 'base-ts-result';
let res = Ok('great success');
console.log(res.unwrap());
res = Err('fatal error');
res.unwrap(); // throws an error
Unwraps result error or throws an error if the result is Ok.
import { Ok, Err } from 'base-ts-result';
let res = Ok('great success');
res.unwrapErr(); // throws an error
res = Err('fatal error');
res.unwrapErr(); // return the error
Unwraps original value or replaces it with provided alternative if the result is Err.
import { Ok, Err } from 'base-ts-result';
let res = Ok('great success');
console.log(res.unwrapOr('not that great')); // prints great success
res = Err('fatal error');
console.log(res.unwrapOr('not that great')); // prints not that great
Unwraps original value if the result is Ok, otherwise returns value from factory function.
let res = Ok(5);
console.log(res.unwrapOrElse(() => 0)); // prints 5
res = Err('error msg');
console.log(res.unwrapOrElse(() => "not a number")); // prints not a number
Unwraps result or throws new Error with provided message.
let res = Ok(5);
console.log(res.expect('provided message')); // 5
res = Err('error msg');
console.log(res.expect('provided message')); // throws an error: 'provided message'
Unwraps error from result or throws new Error with provided message.
let res = Ok(5);
console.log(res.expectErr('provided message')); // Exception: provided message
res = Err('error msg');
console.log(res.expectErr('provided message')); // 'error msg'
Returns true if result is Ok, otherwise returns false.
let res = Ok(5);
console.log(res.isOk()); // true
res = Err('error msg');
console.log(res.isOk()); // false
Returns true if result is Err, otherwise returns false.
let res = Ok(5);
console.log(res.isErr()); // false
res = Err('error msg');
console.log(res.isOk()); // true
Returns value if result is Ok, or undefined if it's Err.
let res = Ok(5);
console.log(res.ok()); // 5
res = Err('error msg');
console.log(res.ok()); // undefined
Returns error value if result is Err, or undefined if it's Ok.
let res = Ok(5);
console.log(res.err()); // undefined
res = Err('error msg');
console.log(res.err()); // error msg
Returns new result with mapped value.
const res = Ok(5);
const mappedRes = res.map((val) => val + 1);
console.log(mappedRes.unwrap()); // 6
Returns new result with mapped error value.
const res = Err('error msg');
const mappedRes = res.mapErr(err => err + ' suffix');
console.log(mappedRes.unwrapErr()); // error msg suffix
Returns new result with mapped ok value & mapped error value.
const mapOk = (val => val + 1);
const mapErr = (err => err + ' suffix');
console.log(Ok(5).unwrap()) // 6
console.log(Err('error msg').unwrapErr()) // error message suffix
Will run provided inspector if result is Ok.
let res = Ok(5);
res.inspect((val) => console.log(val)); // prints 5
res = Err('error msg');
res.inspect((val) => console.log(val)); // prints nothing
Will run provided inspector if result is Err.
let res = Ok(5);
res.inspect((val) => console.log(val)); // prints noting
res = Err('error msg');
res.inspect((val) => console.log(val)); // prints error msg
Returns new async result constructed from current result.
const res = Ok(5);
const asyncRes = res.toAsync();
interface Result<Val, Err> {
// Contained Promise
value: Val | Err;
// Queries
unwrap(): Val;
unwrapErr(): Err;
unwrapOr(altVal: Val): Val;
unwrapOrElse(altValFactory: (err: Err) => Val): Val;
expect(msg: string): Val;
expectErr(msg: string): Err;
isOk(): this is OK<Val>;
isErr(): this is ERR<Err>;
ok(): Val|undefined,
err(): Err|undefined,
// Mappers
map<MappedVal>(mapper: (val: Val) => MappedVal): Result<MappedVal, Err>;
mapOrElse<MappedVal>(mapper: (val: Val) => MappedVal, fallback: (err: Err) => MappedVal): Result<MappedVal, Err>;
mapErr<MappedErr>(mapper: (err: Err) => MappedErr): Result<Val, MappedErr>;
// Utilities
inspect(inspector: (val: Val) => any): Result<Val, Err>;
inspectErr(inspector: (err: Err) => any): Result<Val, Err>;
toAsync(): AsyncResult<Val, Err>;
}
Class that contains operation result and interaction methods for async code
Creates async result from promise.
import { AsyncResult } from 'base-ts-result';
const resPromise = AsyncResult.fromPromise(Promise.resolve(1));
Creates async result from synchronous result.
import { AsyncResult } from 'base-ts-result';
const resPromise = AsyncResult.fromResult(Ok(5));
Creates async result from result promise.
import { AsyncResult } from 'base-ts-result';
const resPromise = AsyncResult.fromResult(Promise.resolve(Ok(5)));
// Async result implementation for more ergonomic usage of Results with async code
class AsyncResult<Val, Err> {
// Contained promise
promise: ResultPromise<Val, Err>;
// Creators
static fromPromise<Val>(promise: Promise<Val>): AsyncResult<Val, unknown>;
static fromResult<Val, Err>(result: Result<Val, Err>): AsyncResult<Val, Err>;
static fromResultPromise<Val, Err>(result: ResultPromise<Val, Err>): AsyncResult<Val, Err>;
// Queries
async unwrap(): Promise<Val>;
async unwrapErr(): Promise<Err>;
async unwrapOr(altVal: Val): Promise<Val>;
async unwrapOrElse(altValFactory: (err: Err) => AsyncMapped<Val>): Promise<Val>;
async expect(msg: string): Promise<Val>;
async expectErr(msg: string): Promise<Err>;
async isOk(): Promise<boolean>;
async isErr(): Promise<boolean>;
async ok(): Promise<Val|undefined>;
async err(): Promise<Err|undefined>;
// Mappers
map<NewVal>(mapper: (val: Val) => AsyncMapped<NewVal>): AsyncResult<NewVal, Err>;
mapOrElse<NewVal>(
mapper: (val: Val) => AsyncMapped<NewVal>,
fallback: (err: Err) => AsyncMapped<NewVal>
): AsyncResult<NewVal, Err>;
mapErr<NewErr>(mapper: (err: Err) => AsyncMapped<NewErr>): AsyncResult<Val, NewErr>;
// Utilities
inspect(inspector: (val: Val) => any): AsyncResult<Val, Err>;
inspectErr(inspector: (err: Err) => any): AsyncResult<Val, Err>;
}
Converts function return value to result.
import { toResult } from 'base-ts-result';
let res = toResult(() => 2);
console.log(res.unwrap()); // prints 2
res = toResult(() => throw new Err());
console.log(res.unwrapErr()); // prints error
Wraps the original function with resultifier.
const rawFn = (a: number) => {
if (a < 0) {
throw new Error('not today')
}
return a;
};
const fn = resultify(
rawFn,
err => err.message
);
const res = fn(-2); // Result<number, string>
res.err() // 'not today'
Converts plain async function to function that return async result.
const rawFn = async (a: number) => {
if (a < 0) {
throw new Error('not today')
}
return a;
};
const fn = asyncResultify(
rawFn,
async err => err.message
); // (a: number) => AsyncResult<number, string>
const res = fn(-2); // AsyncResult<number, string>
await res.err() // 'not today'
Converts function that returns a promise with sync result into function that returns async result.
const resultPromiseFn = async (a: number): ResultPromise<number, string> => {
await new Promise(() => {
setTimeOut(() => Promise.resolve(), 100)
});
if (a < 0) {
return Err('err');
}
return Ok(a);;
};
// Manual, not convenient way
const valueOne = (await resultPromiseFn(2)).unwrap();
// Using helper
const fn = createAsyncResultFn(resultPromiseFn); // (a: number) => AsyncResult<number, string>
const valueTwo = await fn(2).unwrap(); // Same result, with more convenient async handling
Creates result from a promise.
const promise = Promise.reject();
const result = toAsyncResult(promise);