npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

safe-result

v1.0.2

Published

Create safe results that are either successes or failures

Downloads

83

Readme

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'
  • function success<T>(value: T): SuccessResult<T>

    Method for creating a successful result.

  • function failure<T>(error: T): FailureResult<T>

    Method for creating a failed result.

  • type Result<T = unknown, E = Error> = SuccessResult<T> | FailureResult<E>

    Type that can be used as return type in methods returning either a SuccessResult or FailureResult.

    function myFunc(ok: boolean): Result<{ name: string; age: number }> {
      return ok
        ? success({ name: 'John Doe', age: 54 })
        : failure(new Error('Bad'))
    }
  • type AsyncResult<T = unknown, E = Error> = Promise<Result<T, E>>

    Same as writing Promise<Result<T, E>>.

  • async function all<T>(values: (T | PromiseLike<T>)[]): Promise<Result<T[]>>

    Behaves like Promise.all() except it doesn't throw. If all values are resolved this promise will resolve to a SuccessResult where the result property is an array of the resolved values.

    If one value fails, this promise will immediately resolve to a FailureResult where the error property is the error from the rejected promise.

  • async function allSetteled<T>(values: Array<T | Promise<T>>): Promise<SuccessAndFailureResult<T[], Error[]>>

    Just like Promise.allSettled() this will wait for all values to either resolve or reject. This will always return a SuccessAndFailureResult instance where both the result and error properties can be "truthy".

  • interface ResultType

    Both SuccessResult, FailureResult and SuccessAndFailureResult implements this interface.

    • ResultType.result Property of the successful value

    • ResultType.error Property of the failed value

    • ResultType.unwrap(): [successValue, failValue] Returns a tuple where the first index is the successful value and the second the failed value. For a SuccessResult this is always [successfulValue, undefined], and for a FailureResult it's always [undefined, failedValue].

      The SuccessAndFailureResult returned from allSetteled() will always be a [[...successfulValues], [...failedValues]] tuple, so neither index will be undefined, but can be an array of length 0.

    • ResultType.success If true it's a successful value

    • ResultType.failure If true it's a failed value

      For SuccessResult and FailureResult the success and failure properties are mutually exclusive.

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