A typescript implementation of Rust's Result object.
A typescript implementation of Rust's Result and Option objects.
Brings compile-time error checking and optional values to typescript.
$ npm install ts-results
or
$ yarn add ts-results
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; }
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; }
import { Result, Err, Ok } from 'ts-results';
let okResult: Result = Ok(10); let okResult2 = Ok(10); // Exact same as abovelet errorResult: Result = Ok(new Error('bad number!')); let errorResult2 = Ok(new Error('bad number!')); // Exact same as above
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; }
let goodResult = new Ok(1); let badResult = new Err(new Error('something went wrong'));goodResult.unwrap(); // 1 badResult.unwrap(); // throws Error("something went wrong")
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")
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")
Deprecated in favor of unwrapOr
let goodResult = Ok(1); let badResult = Err(new Error('something went wrong'));goodResult.unwrapOr(5); // 1 badResult.unwrapOr(5); // 5
function checkIsValid(isValid: boolean): Result { if (isValid) { return Ok.EMPTY; } else { return new Err(new Error('Not valid')); } }
ts-resultshas two helper functions for operating over n
Resultobjects.
Either returns all of the
Okvalues, or the first
Errvalue
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.
Either returns the first
Okvalue, or all
Errvalues
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
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
import { resultMapErr } from 'ts-results/rxjs-operators';
Behaves exactly the same as resultMap, but maps the error value.
import { resultMapTo } from 'ts-results/rxjs-operators';
Behaves the same as resultMap, but takes a value instead of a function.
import { resultMapErrTo } from 'ts-results/rxjs-operators';
Behaves the same as resultMapErr, but takes a value instead of a function.
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
import { elseMapTo } from 'ts-results/rxjs-operators';
Behaves the same as elseMap, but takes a value instead of a function.
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
Resultobjects onto a stream of objects turns the stream into a stream of
Resultobjects.
Merging or switching from a stream of
Resultobjects onto a stream of
Resultobjects turn the stream into a stream of
Resultobjects.
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
Converts an
Observable>to an
Observbleby 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
Converts an
Observable>to an
Observbleby 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