Options
All
  • Public
  • Public/Protected
  • All
Menu

safe-result

safe-result

One of the more challenging things in software development is error handling. When should an error be throw? When should it be caught? Should I catch it silently and return "undefined"? But if I do that someone higher up the callstack might want to get hold of the error for logging and what not.

The purpose of this library is trying to make this a bit more coherent. Languages like Rust and Go have built-in mechanisms to make error handling a bit more concise and coherent than using try / catch, which imho litters the the code enormously.

What more is, you often need to resort to using let instead of const just because you need to use the value after try / catch.

The usecase for this library is to be used in you own library functions in your application. There really is no way around resorting to try / catch in Javascript, so you will have to use try / catch in your own "low-level" code.

API

Full API documentation

You can either import the module via the default import

import Result from 'safe-result'

or only import the methods you need

import { success, failure } from 'safe-result'

Examples

Single values

A single successful value is represented by an instance of SuccessResult. A single failed value is represented by an instance of FailureResult.

Instances of these classes can be created by calling success(okValue) and failure(Error) respectively.

import Result from 'safe-result'

function unsafeMethod(): Result.Result<{ name: string; age: number }> {
  try {
    return Result.success(callToMethodThatMightThrow())
  } catch (e) {
    return Result.failure(e)
  }
}

// Method one
const r1 = unsafeMethod()

if (r1.success) {
  console.log('Got OK result', r1.result) // {name: 'Some string', age: x}
} else {
  console.log('Got failed result:', r1.error) // Error('some error')
}

// Method 2
const [result, error] = unsafeMethod().unwrap()

if (error) {
  console.error(error.message)
  return
}

console.log(`Name:`, result.name)

Async collections

Result.all()

safe-result sort of implements Promise.all(), except it doesn't throw when a value is rejected. Instead you get back a FailureResult on error and a SuccessResult if all promises were resovled.

import Result, { success, failure } from 'safe-result'

const args = [Promise.resolve(1), Promise.resovle(2)]
const [r1, e1] = (await Result.all(args)).unwrap()

if (e1) {
  console.error(`An error occured:`, e1.message)
  return
}

console.log(r1) // [1, 2]

Note: Result.all() doesn't neccessarily have to be given Promises or Promise<Result>-objects as arguments. If an argument isn't a promise, one will be created.

Note: If the value array given to Result.all() are of different types you have to explicitly state the types of elements in the array argument for the type inference of the result to be correct. This however is no different from how Promise.all() behaves.

const args: Promise<number | string>[] = [
  Promise.resovle(1),
  Promise.resolve('two'),
]

const [res] = (await Result.all(args)).unwrap()

if (res) {
  // Infered type of `res` here will be `Array<string | number>`
}

...

Result.allSettled()

This works pretty much the same as Promise.allSettled() except you get back a SuccessAndFailureResult instance. The way this differs from the SuccessResult and FailureResult classes is that this can be both a success and a failure at the same time.

const args = [Promise.resovle(1), Promise.reject(new Error('Fail'))]

const res = await Result.allSettled(args)

if (res.success) {
  // Will be true in this case
  // res.result -> [1]
}

if (res.failure) {
  // Will also be true in this case
  // res.error -> [Error('Fail')]
}

// If you simply want to ignore eventual errors

const [res] = (await Result.allSettled(args)).unwrap()

Generated using TypeDoc