Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/poppa/safe-result

Value that is either a success or failure
https://github.com/poppa/safe-result

javascript-library js nodejs typesafety typescript

Last synced: 2 months ago
JSON representation

Value that is either a success or failure

Awesome Lists containing this project

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](https://poppa.github.io/safe-result/)**

You can either import the module via the default import

```ts
import Result from 'safe-result'
```

or only import the methods you need

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

- #### `function success(value: T): SuccessResult`

Method for creating a successful result.

- #### `function failure(error: T): FailureResult`

Method for creating a failed result.

- #### `type Result = SuccessResult | FailureResult`

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

```ts
function myFunc(ok: boolean): Result<{ name: string; age: number }> {
return ok
? success({ name: 'John Doe', age: 54 })
: failure(new Error('Bad'))
}
```

- #### `type AsyncResult = Promise>`

Same as writing `Promise>`.

- #### `async function all(values: (T | PromiseLike)[]): Promise>`

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(values: Array>): Promise>`

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.

```ts
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.

```ts
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`-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.

```ts
const args: Promise[] = [
Promise.resovle(1),
Promise.resolve('two'),
]

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

if (res) {
// Infered type of `res` here will be `Array`
}
```

...

#### `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.

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