This project was developed to wrap processes that might raise exceptions and to handle the results declaratively and safely. The basic control syntax of TypeScript is a "statement". Therefore, the scope of an expression changes before and after a statement, and the process of initializing -> assigning a variable is written. Not only in TypeScript, but also in classical procedural programming, you will often write code like the following
let res;
try {
res = doSomething();
} catch (e) {
// log
}
Variables that allow assignment can be dangerous to work with, since you must be constantly aware of the state of the variable as the procedure progresses. As you can see from the examples in this project, it is possible to handle such dangerous variables in an immutable way.
This project is strongly influenced by kotlin.Result and scala.util.Try In particular, it is heavily influenced by kotlin.Result, and generally implements the same methods. However, the methods that can be easily described in kotlin and scala are a bit too cumbersome.
From npm registry...
npm i @snozaki/resultify
type declaration is included.
This repository is moved to @snozaki/resultify
, so ResultT
is depricated.
You can simply introduce this library by import.
Try to import runCatching
and pass a higher function to wrap the result.
import { runCatching } from 'resultt';
const result = runCatching(() => execute());
Given sample logic and response interface...
interface Response {
data: string
}
class Service {
execute(value: string): Response {
return {data: value};
}
}
Try to start from runCatching
method. This wraps the result of success or failure.
We can handle values to call like getOrThrow
, getOrElse
, getOrDefault
and so on.
// execute some function and wrap by "runCatching"
const result: Resultt<Response> = runCatching(() => new Service().execute('execution'))
// It should be executed "onSuccess".
.onSuccess((v) => {
console.log(`response => ${v}`);
});
.onFailure((it: Error) => {
console.error(it);
})
.andLastly(() => console.log('End service calling'));
// You may get the value of execute by "get" functions as declarative.
const v1 = result.getOrThrow(); // => success ... { data: "execution" }
const v2 = result.getOrDefault({
data: 'OTHER'
});
// You can process as commandly.
let v;
if (result.isSuccess()) {
v = result.getOrThrow();
}
On the way to get the raw value, we can insert some intermediate processes by onSucess
or onFailure
.
This project provides some helpers for higher kind functions to shortcut like a function () => T
.
const r = runCatching(supply(new Application().execute()))
.fold(
supply('success'),
onErrorThen('failure'),
);
supply
... create a function that return value instantly.onErrorThen
... create a function that partially applyv
and return value.
For filters, eq
and ne
can be used. eq
and ne
compare recursively, so that it is possible to compare object or array.
const r = new Resultt('unittest').filter(eq('unit'));
The process wrapped Resultt
can fold or map another value or Resultt
.
// Map the result to another map by fold.
const folded = runCatching(() => (new Service().execute('execution')))
.fold(
(data: Response) => {
console.log(data);
return data.data.length;
},
(it: Error) => {
console.log(it);
return 0;
},
);
console.log(folded); // => 9
// Or, shorthand for fold with getOrElse
const n: number = runCatching(() => {
return new Service().execute('execution');
})
.getOrElse((it: Error) => {
console.log(it);
return 0;
},
);
console.log(n); // => 9
In the same way of map
and fold
, recover
also provides mapping funtion when the original expression falls into Failure
.
const r = runCatching(() => new Service().execute('execution'))
.recover((e: Error) => ({
data: 'RECOVERED',
}))
.getOrThrow();
console.log(r); // => { data: 'RECOVERED' }
recover
does not catch Error when the transformation function throws Error. If it wraps by Resultt
, use recoverCatching
.
Even though an original expression or block is executed successfully, a result value is not always expected value. The executed value is tested by filter
like below.
const r = runCatching(() =>
(new Service().execute('execution')))
.filter((t) => t.data.length > 10)
.getOrElse(() => ({data: 'message is under 10'}));
console.log(r); // => {data: 'message is under 10'}
filter
method can be used with some helper eq
(equal) and ne
(not equal). eq
and ne
can check deeply equally.
const r = runCatching(() =>
(new Service().execute('execution')))
.filter(eq({data: 'execution'}));
console.log(r.isSuccess()); // => true
Full class documentation is here: docs