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
164 Stars 8 Forks MIT License 55 Commits 2 Opened issues

Description

A typescript implementation of Rust's Result object.

Services available

!
?

Need anything else?

Contributors list

# 150,263
TypeScr...
iOS
Android
Angular
29 commits
# 35,595
TypeScr...
lodash
privacy...
Google
16 commits
# 107,948
GraphQL
express...
js
IPFS
1 commit
# 42,150
Nest
TypeScr...
web-cra...
web-scr...
1 commit
# 293
JavaScr...
HTML
React
gatsby
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 okResult2 = Ok(10); // Exact same as above

let errorResult: Result = Ok(new Error('bad number!')); let errorResult2 = Ok(new Error('bad number!')); // Exact same as above

Type Safety

let 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; }

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.