ts-results
A typescript implementation of Rust's Result object. Brings compile-time error checking to typescript.
Note: This is the documentation for the newly released ts-results@2.0.0
with breaking changes. To see breaking changes, go to CHANGELOG.md
Contents
Installation
$ npm install ts-results
or
$ yarn install ts-results
Example
Convert this:
import {existsSync, readFileSync} from 'fs';
function readFile(path: string): string {
if (existsSync(path)) {
return readFileSync(path);
} else {
throw new Error('invalid path');
}
}
const text = readFile('test.txt');
To this:
import {existsSync, readFileSync} from 'fs';
import {Ok, Err, Result} from 'ts-results';
function readFile(path: string): Result<string, 'invalid path'> {
if (existsSync(path)) {
return new Ok(readFileSync(path));
} else {
return new Err("invalid path");
}
}
const result = readFile('test.txt');
if (result.ok) {
const text = result.val;
} else {
const err = result.val;
}
Usage
import { Result, Err, Ok, Results } from 'ts-results';
Creation
let okResult: Result<number, Error> = new Ok(10);
let okResult2 = Ok<number, Error>(10);
let errorResult: Result<number, Error> = new Ok(new Error('bad number!'));
let errorResult2 = Ok<number, Error>(new Error('bad number!'));
Type Safety
let result = Ok<number, Error>(1);
if (result.ok) {
let number = result.val + 1;
} else {
console.error(result.val.message);
}
if (result.err) {
console.error(result.val.message);
} else {
let number = result.val + 1;
}
Unwrap
let goodResult = new Ok(1);
let badResult = new Err(new Error("something went wrong"));
goodResult.unwrap();
badResult.unwrap();
Expect
let goodResult = Ok<number, Error>(1);
let badResult = Err<number, Error>(new Error("something went wrong"));
goodResult.expect('goodResult should be a number');
badResult.expect('badResult should be a number');
Map and MapErr
let goodResult = new Ok(1);
let badResult = new Err(new Error("something went wrong"));
goodResult.map(num => num + 1).unwrap();
badResult.map(num => num + 1).unwrap();
goodResult.map(num => num + 1).mapErr(err => new Error('mapped')).unwrap();
badResult.map(num => num + 1).mapErr(err => new Error('mapped')).unwrap();
Else
let goodResult = new Ok(1);
let badResult = new Err(new Error("something went wrong"));
goodResult.else(5);
badResult.else(5);
Empty
function checkIsValid(isValid: boolean): Result<void, Error> {
if (isValid) {
return Ok.EMPTY;
} else {
return new Err(new Error("Not valid"))
}
}
Combining Results
There may be some cases where we have two or more separate Result
objects and we want to do something with both values.
This is handled by using the Results
function to combine them.
let pizzaResult: Result<Pizza, GetPizzaError> = getPizzaSomehow();
let toppingsResult: Result<Toppings, GetToppingsError> = getToppingsSomehow();
let result = Results(pizzaResult, toppingsResult);
let [pizza, toppings] = result.unwrap();
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<Result<number, Error>> = of(new Ok(5), new Err('uh oh'));
const greaterThanZero = obs$.pipe(
resultMap(number => number > 0),
);
greaterThanZero.subscribe(result => {
if (result.ok) {
console.log('Was greater than zero: ' + result.val);
} else {
console.log('Got Error Message: ' + result.val);
}
});
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<Result<number, Error>> = of(new Ok(5), new Err(new Error('uh oh')));
const doubled = obs$.pipe(
elseMap(err => {
console.log('Got error: ' + err.message);
return -1;
})
);
doubled.subscribe(number => {
console.log('Got number: ' + number);
});
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<T, E>
objects onto a stream of <T2>
objects turns the stream into a stream of Result<T2, E>
objects.
Merging or switching from a stream of Result<T, E>
objects onto a stream of Result<T2, E2>
objects turn the stream into a stream of Result<T2, E | T2>
objects.
import {of, Observable} from 'rxjs';
import {Ok, Err, Result} from 'ts-results';
import {resultMergeMap} from 'ts-results/rxjs-operators';
const obs$: Observable<Result<number, Error>> = of(new Ok(5), new Err(new Error('uh oh')));
const obs2$: Observable<Result<string, CustomError>> = of(new Ok('hi'), new Err(new CustomError('custom error')));
const test$ = obs$.pipe(
resultMergeMap(number => {
console.log('Got number: ' + number);
return obs2$;
})
);
test$.subscribe(result => {
if (result.ok) {
console.log('Got string: ' + result.val);
} else {
console.log('Got error: ' + result.val.message);
}
});
filterResultOk
Converts an Observable<Result<T, E>>
to an Observble<T>
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<Result<number, Error>> = of(new Ok(5), new Err(new Error('uh oh')));
const test$ = obs$.pipe(
filterResultOk()
);
test$.subscribe(result => {
console.log('Got number: ' + result);
});
filterResultErr
Converts an Observable<Result<T, E>>
to an Observble<T>
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<Result<number, Error>> = of(new Ok(5), new Err(new Error('uh oh')));
const test$ = obs$.pipe(
filterResultOk()
);
test$.subscribe(result => {
console.log('Got number: ' + result);
});