@finibit/result

0.1.1 • Public • Published

@finibit/result

Handle function call results using a common type

Installation

Requires Node.js version >=16.15.0.

Install using NPM:

npm i @finibit/result

Usage

Import Result constructor using import statement:

import Result from '@finibit/result'

The constructor accepts a single function, and an optional list of arguments. The function passed to the Result constructor is called immediately, and the result of that call can be later accessed through properties of the returned Result instance:

const result = Result(() => { /* ... */ })
// access result

You can construct Result instances with or without new, which means that new Result() is the same as Result().

Accessing results

Values are objects returned by functions passed to the Result constructor. Exceptions thrown by these functions are not values, they are errors. If a function returns a value, it is said that Result contains a value. Similarly, if a function throws an error, it is said that Result contains an error.

You can check whether a particular Result instance contains a value or an error by reading result.hasValue and result.hasError properties:

const ok = Result(() => 'foo')
ok.hasValue === true
ok.hasError === false

const nok = Result(() => { throw 'foo' })
nok.hasValue === false
nok.hasError === true

You can access the contained value or error by calling result.value or result.error getters:

const ok = Result(() => 'foo')
ok.value === 'foo'

const nok = Result(() => { throw 'foo' })
nok.error === 'foo'

If you try to access value of a result that contains error, that error will be thrown. Similarly, when you try to access error of a result that contains value, thenError('Bad result access') error will be thrown.

You can use the special method result.valueOr to get the contained value, or the given fallback value, without throwing any errors:

const ok = Result(() => 'foo')
ok.valueOr('bar') === 'foo'

const nok = Result(() => { throw 'foo' })
nok.valueOr('bar') === 'bar'

Finally, you can unwrap a particular Result instance by calling result.either getter. It will return whatever is contained without throwing errors:

const ok = Result(() => 'foo')
ok.either === 'foo'

const nok = Result(() => { throw 'foo' })
nok.either === 'foo'

Returning results

It is possible for functions to return Result instances directly. This allows the caller's code to avoid manually wrapping those functions with the Result constructor:

// foo.js
const foo = () => Result(() => { /* ... */ })
export default foo
// main.js
import foo from './foo.js'
const result = foo()
// access result

Alternatively, you can use Result.wrap helper function to create a function that returns Result instances:

// foo.js
const foo = Result.wrap(() => { /* ... */ })
export default foo
// main.js
import foo from './foo.js'
const result = foo()
// access result

There's a few helper functions that ease creation of Result instances. You can construct a Result instance containing a value or error using Result.ok or Result.fail static methods (similar to Promise.resolve and Promise.reject):

const ok = Result.ok('foo')
ok.value === 'foo'

const nok = Result.nok('foo')
ok.error === 'foo'

Collapsing results

Functions passed to the Result constructor can themselves return Result instances. In such scenarios, a chain of results will collapse into a single result:

const ok = Result(() => Result(() => 'foo'))
ok.value === 'foo'

const nok = Result(() => Result(() => { throw 'foo' }))
nok.error === 'foo'

The same happens if a function throws a Result instance:

const ok = Result(() => { throw Result(() => 'foo') })
ok.value === 'foo'

const nok = Result(() => { throw Result(() => { throw 'foo' }) })
nok.error === 'foo'

Notice that throwing a Result instance that contains a value results in the outermost Result instance to also contain a value instead of an error. Throwing Result instances is treated the same way as returning Result instances.

Working with promises

You can wrap promises in Result instances using Result.promise static method:

const ok = await Result.promise(Promise.resolve('foo'))
ok.value === 'foo'

const nok = await Result.promise(Promise.reject('foo'))
nok.error === 'foo'

With this approach you can avoid the burden of writing try...catch blocks, and replace them with conditionals:

import { readFile } from 'fs/promises'

try {
  const content = await readFile('foo.json', 'utf8')
  // parse json
} catch (error) {
  // handle the error
}
import { readFile } from 'fs/promises'

const result = await Result.promise(readFile('foo.txt', 'utf8'))
if (result.hasValue) {
  // parse json
} else {
  // handle the error
}

Package Sidebar

Install

npm i @finibit/result

Weekly Downloads

1

Version

0.1.1

License

MIT

Unpacked Size

13.4 kB

Total Files

5

Last publish

Collaborators

  • dsobczyk