Need help with ts-results?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

vultix
296 Stars 18 Forks MIT License 68 Commits 16 Opened issues

Description

A typescript implementation of Rust's Result object.

Services available

!
?

Need anything else?

Contributors list

# 143,163
CSS
iOS
Angular
angular...
37 commits
# 31,734
TypeScr...
lodash
privacy...
Google
18 commits
# 315
JavaScr...
HTML
React
gatsby
2 commits
# 109,660
GraphQL
express...
js
IPFS
1 commit
# 41,046
Nest
TypeScr...
web-cra...
web-scr...
1 commit

ts-results

A typescript implementation of Rust's Result and Option objects.

Brings compile-time error checking and optional values to typescript.

Contents

Installation

$ npm install ts-results

or

$ yarn add ts-results

Example

Result Example

Convert this:

import { existsSync, readFileSync } from 'fs';

function readFile(path: string): string { if (existsSync(path)) { return readFileSync(path); } else { // Callers of readFile have no way of knowing the function can fail throw new Error('invalid path'); } }

// This line may fail unexpectedly without warnings from typescript const text = readFile('test.txt');

To this:

import { existsSync, readFileSync } from 'fs';
import { Ok, Err, Result } from 'ts-results';

function readFile(path: string): Result { if (existsSync(path)) { return new Ok(readFileSync(path)); // new is optional here } else { return new Err('invalid path'); // new is optional here } }

// Typescript now forces you to check whether you have a valid result at compile time. const result = readFile('test.txt'); if (result.ok) { // text contains the file's content const text = result.val; } else { // err equals 'invalid path' const err = result.val; }

Option Example

Convert this:

declare function getLoggedInUsername(): string | undefined;

declare function getImageURLForUsername(username: string): string | undefined;

function getLoggedInImageURL(): string | undefined { const username = getLoggedInUsername(); if (!username) { return undefined; }

return getImageURLForUsername(username);

}

const stringUrl = getLoggedInImageURL(); const optionalUrl = stringUrl ? new URL(stringUrl) : undefined; console.log(optionalUrl);

To this:

import { Option, Some, None } from 'ts-results';

declare function getLoggedInUsername(): Option;

declare function getImageForUsername(username: string): Option;

function getLoggedInImage(): Option { return getLoggedInUsername().andThen(getImageForUsername); }

const optionalUrl = getLoggedInImage().map((url) => new URL(stringUrl)); console.log(optionalUrl); // Some(URL('...'))

// To extract the value, do this: if (optionalUrl.some) { const url: URL = optionalUrl.val; }

Usage

import { Result, Err, Ok } from 'ts-results';

Creation

let okResult: Result = Ok(10);
let errorResult: Result = Err(new Error('bad number!'));

Type Safety

Note: Typescript currently has a bug, making this type narrowing only work when

strictNullChecks
is turned on. ``

typescript
let result: Result = Ok(1);
if (result.ok) {
    // Typescript knows that result.val is a number because result.ok was true
    let number = result.val + 1;
} else {
    // Typescript knows that result.val is an
Error` because result.ok was false console.error(result.val.message); }

if (result.err) { // Typescript knows that result.val is an

Error
because result.err was true console.error(result.val.message); } else { // Typescript knows that result.val is a number because result.err was false let number = result.val + 1; } ```

Stack Trace

A stack trace is generated when an

Err
is created.
let error = Err('Uh Oh');
let stack = error.stack;

Unwrap

let goodResult = new Ok(1);
let badResult = new Err(new Error('something went wrong'));

goodResult.unwrap(); // 1 badResult.unwrap(); // throws Error("something went wrong")

Expect

let goodResult = Ok(1);
let badResult = Err(new Error('something went wrong'));

goodResult.expect('goodResult should be a number'); // 1 badResult.expect('badResult should be a number'); // throws Error("badResult should be a number - Error: something went wrong")

Map and MapErr

let goodResult = Ok(1);
let badResult = Err(new Error('something went wrong'));

goodResult.map((num) => num + 1).unwrap(); // 2 badResult.map((num) => num + 1).unwrap(); // throws Error("something went wrong")

goodResult .map((num) => num + 1) .mapErr((err) => new Error('mapped')) .unwrap(); // 2 badResult .map((num) => num + 1) .mapErr((err) => new Error('mapped')) .unwrap(); // throws Error("mapped")

Else

Deprecated in favor of unwrapOr

UnwrapOr

let goodResult = Ok(1);
let badResult = Err(new Error('something went wrong'));

goodResult.unwrapOr(5); // 1 badResult.unwrapOr(5); // 5

Empty

function checkIsValid(isValid: boolean): Result {
    if (isValid) {
        return Ok.EMPTY;
    } else {
        return new Err(new Error('Not valid'));
    }
}

Combining Results

ts-results
has two helper functions for operating over n
Result
objects.
Result.all

Either returns all of the

Ok
values, or the first
Err
value
let pizzaResult: Result = getPizzaSomehow();
let toppingsResult: Result = getToppingsSomehow();

let result = Result.all(pizzaResult, toppingsResult); // Result

let [pizza, toppings] = result.unwrap(); // pizza is a Pizza, toppings is a Toppings. Could throw GetPizzaError or GetToppingsError.

Result.any

Either returns the first

Ok
value, or all
Err
values
let url1: Result = attempt1();
let url2: Result = attempt2();
let url3: Result = attempt3();

let result = Result.any(url1, url2, url3); // Result

let url = result.unwrap(); // At least one attempt gave us a successful url

Usage with rxjs

resultMap

Allows you to do the same actions as the normal rxjs map operator on a stream of Result objects.

import { of, Observable } from 'rxjs';
import { Ok, Err, Result } from 'ts-results';
import { resultMap } from 'ts-results/rxjs-operators';

const obs$: Observable> = of(Ok(5), Err('uh oh'));

const greaterThanZero = obs$.pipe( resultMap((number) => number > 0), // Doubles the value ); // Has type Observable>

greaterThanZero.subscribe((result) => { if (result.ok) { console.log('Was greater than zero: ' + result.val); } else { console.log('Got Error Message: ' + result.val); } });

// Logs the following: // Got number: 10 // Got Error Message: uh oh

resultMapErr

import { resultMapErr } from 'ts-results/rxjs-operators';

Behaves exactly the same as resultMap, but maps the error value.

resultMapTo

import { resultMapTo } from 'ts-results/rxjs-operators';

Behaves the same as resultMap, but takes a value instead of a function.

resultMapErrTo

import { resultMapErrTo } from 'ts-results/rxjs-operators';

Behaves the same as resultMapErr, but takes a value instead of a function.

elseMap

Allows you to turn a stream of Result objects into a stream of values, transforming any errors into a value.

Similar to calling the else function, but works on a stream of Result objects.

import { of, Observable } from 'rxjs';
import { Ok, Err, Result } from 'ts-results';
import { elseMap } from 'ts-results/rxjs-operators';

const obs$: Observable> = of(Ok(5), Err(new Error('uh oh')));

const doubled = obs$.pipe( elseMap((err) => { console.log('Got error: ' + err.message);

    return -1;
}),

); // Has type Observable

doubled.subscribe((number) => { console.log('Got number: ' + number); });

// Logs the following: // Got number: 5 // Got error: uh oh // Got number: -1

elseMapTo

import { elseMapTo } from 'ts-results/rxjs-operators';

Behaves the same as elseMap, but takes a value instead of a function.

resultSwitchMap and resultMergeMap

Allows you to do the same actions as the normal rxjs switchMap and rxjs switchMap operator on a stream of Result objects.

Merging or switching from a stream of

Result
objects onto a stream of
 objects turns the stream into a
stream of 
Result
objects.

Merging or switching from a stream of

Result
objects onto a stream of
Result
objects turn the stream into a stream of
Result
objects.
import { of, Observable } from 'rxjs';
import { Ok, Err, Result } from 'ts-results';
import { resultMergeMap } from 'ts-results/rxjs-operators';

const obs$: Observable> = of(new Ok(5), new Err(new Error('uh oh')));

const obs2$: Observable> = of(new Ok('hi'), new Err(new CustomError('custom error')));

const test$ = obs$.pipe( resultMergeMap((number) => { console.log('Got number: ' + number);

    return obs2$;
}),

); // Has type Observable>

test$.subscribe((result) => { if (result.ok) { console.log('Got string: ' + result.val); } else { console.log('Got error: ' + result.val.message); } });

// Logs the following: // Got number: 5 // Got string: hi // Got error: custom error // Got error: uh oh

filterResultOk

Converts an

Observable>
to an
Observble
by filtering out the Errs and mapping to the Ok values.
import { of, Observable } from 'rxjs';
import { Ok, Err, Result } from 'ts-results';
import { filterResultOk } from 'ts-results/rxjs-operators';

const obs$: Observable> = of(new Ok(5), new Err(new Error('uh oh')));

const test$ = obs$.pipe(filterResultOk()); // Has type Observable

test$.subscribe((result) => { console.log('Got number: ' + result); });

// Logs the following: // Got number: 5

filterResultErr

Converts an

Observable>
to an
Observble
by filtering out the Oks and mapping to the error values.
import { of, Observable } from 'rxjs';
import { Ok, Err, Result } from 'ts-results';
import { filterResultOk } from 'ts-results/rxjs-operators';

const obs$: Observable> = of(new Ok(5), new Err(new Error('uh oh')));

const test$ = obs$.pipe(filterResultOk()); // Has type Observable

test$.subscribe((result) => { console.log('Got number: ' + result); });

// Logs the following: // Got number: 5

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.