Skip to content

Kostayne/base-ts-result

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Base Ts Result

Better error handling stolen from rust. Return result values instead of throwing exceptions. Handle every error result with minimum nesting.

npm npm NPM

Install

npm i base-ts-result
yarn add base-ts-result
pnpm add base-ts-result

Example

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);

Docs

Constructors

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');

Result

Interface that contains operation result and interaction methods

Result.unwrap()

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

Result.unwrapErr()

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

Result.unwrapOr(altValue)

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

Result.unwrapOrElse(factory: (err: Err))

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

Result.expect(message)

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'

Result.expectError(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'

Result.isOk()

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

Result.isErr()

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

Result.ok()

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

Result.err()

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

Result.map(mapper)

Returns new result with mapped value.

const res = Ok(5);
const mappedRes = res.map((val) => val + 1);

console.log(mappedRes.unwrap()); // 6

Result.mapErr(mapper)

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

Result.mapOrElse(mapper, fallback)

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

Result.inspect()

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

Result.inspectErr()

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

Result.toAsync()

Returns new async result constructed from current result.

const res = Ok(5);
const asyncRes = res.toAsync();

Result interface

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>;
}

AsyncResult

Class that contains operation result and interaction methods for async code

Static AsyncResult.fromPromise(promise)

Creates async result from promise.

import { AsyncResult } from 'base-ts-result';

const resPromise = AsyncResult.fromPromise(Promise.resolve(1));

Static AsyncResult.fromResult(result)

Creates async result from synchronous result.

import { AsyncResult } from 'base-ts-result';

const resPromise = AsyncResult.fromResult(Ok(5));

Static AsyncResult.fromResultPromise(resultPromise)

Creates async result from result promise.

import { AsyncResult } from 'base-ts-result';

const resPromise = AsyncResult.fromResult(Promise.resolve(Ok(5)));

AsyncResult interface

// 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>;
}

Helpers

toResult(fn)

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

resultify(fn, mapErr)

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'

asyncResultify(fn, mapErr)

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'

createAsyncResult(fn: () => Promise)

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

toAsyncResult(promise)

Creates result from a promise.

const promise = Promise.reject();
const result = toAsyncResult(promise);

Sources

About

Better error handling stolen from rust

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published