@lonli-lokli/ts-result v2.4.0
Result Monad - monad represents some result in different states - Initial, Pending, Success and Failure.
Inspired by RemoteData type, but depends not on fp-ts but on smaller @sweet-monads
Explanation
The problem it solving present is a very common one. You are loading a list of things but instead of showing a loading indicator you just see zero items. Same happens with actual data loading - sometimes you just do not think it will fail.
In my mental model, REST requests have one of four states:
- We haven't asked yet.
- We've asked, but we haven't got a response yet.
- We got a response, but it was an error.
- We got a response, and it was the data we wanted.
That is the purpose of this library - allow clean usage over this states.
Better explanation your can read in the artice How Elm Slays a UI Antipattern
Installation
npm install @lonli-lokli/ts-result
Usage
import { Result, success } from "@lonli-lokli/ts-result";
class UserNotFoundError extends Error {
name: "UserNotFoundError";
}
type User = { email: string; password: string };
function getUser(id: number): Result<UserNotFoundError, User> {
return success({ email: "test@gmail.com", password: "test" });
}
// Result<UserNotFoundError, string>
const user = getUser(1).map(({ email }) => email);API
chainmergemergeInOnemergeInManyinitialpendingfailuresuccessfromfromPromisefromTryfromMaybefromEitherfromNullableisResultResult#isInitialResult#isPendingResult#isFailureResult#isSuccessResult#orResult#joinResult#mapResult#mapSuccessResult#mapFailureResult#asyncMapResult#applyResult#asyncApplyResult#chainResult#asyncChainResult#toEitherResult#toMaybeResult#toNullableResult#toUndefinedResult#unwrapResult#foldHelpers
chain
function chain<F, S, NF, NS>(fn: (v: S) => Promise<Result<NF, NS>>): (m: Result<F, s>) => Promise<Result<F | NF, NS>>;fn: (v: S) => Promise<Result<NF, NS>>- function which should be applied asynchronously toResult<F, S>value- Returns function with
Result<F, S>argument and promisiedResultwith new error or mapped byfnvalue (could be used insidePromise#thenfunction).
Example:
const getValue = async () => success(1);
// Result<TypeError, number>
const result = await getValue()
.then(chain(async v => success(v * 2)))
.then(chain(async g => failure(new TypeError("Unexpected"))));merge
Alias for mergeInOne
function merge<F1, S1>(values: [Result<F1, S1>]): Result<F1, [S1]>;
function merge<F1, S1, F2, S2>(values: [Result<F1, S1>, Result<F2, S2>]): Result<F1 | F2, [S1, S2]>;
function merge<F1, S1, F2, S2, F3, S3>(
values: [Result<F1, S1>, Result<F2, S2>, Result<F3, S3>]
): Result<F1 | F2 | F3, [S1, S2, S3]>;
// ... until 10 elementsvalues: Array<Result<F, S>>- Array of Result values which will be merged into Result of Array- Returns
Result<F, Array<S>>which will containSuccess<Array<S>>if all of array elements wasSuccess<R>,Failure<F>if all of array elements wasFailure<F>,Initialif at least oneInitial, otherwisePending.
Example:
const v1 = initial; // Result<never, never>.Initial
const v2 = pending; // Result<never, never>.Pending
const v3 = success<TypeError, number>(2); // Result<TypeError, number>.Success
const v4 = success<ReferenceError, string>("test"); // Result<ReferenceError, string>.Success
const v5 = failure<Error, boolean>(new Error()); // Result<Error, boolean>.Failure
const r1 = merge([v1, v2]); // Result<never, [number, number]>.Initial
const r2 = merge([v2, v5]); // Result<Error, [never, boolean]>.Pending
const r3 = merge([v3, v4]); // Result<TypeError | ReferenceError, [number, string]>.Success
const r4 = merge([v3, v4, v5]); // Result<TypeError | Error | ReferenceError, [number, string, boolean]>.FailuremergeInOne
function merge<F1, S1>(values: [Result<F1, S1>]): Result<F1, [S1]>;
function merge<F1, S1, F2, S2>(values: [Result<F1, S1>, Result<F2, S2>]): Result<F1 | F2, [S1, S2]>;
function merge<F1, S1, F2, S2, F3, S3>(
values: [Result<F1, S1>, Result<F2, S2>, Result<F3, S3>]
): Result<F1 | F2 | F3, [S1, S2, S3]>;
// ... until 10 elementsvalues: Array<Result<F, S>>- Array of Result values which will be merged into Result of Array- Returns
Result<F, Array<S>>which will containSuccess<Array<S>>if all of array elements wasSuccess<R>,Failure<F>if all of array elements wasFailure<F>,Initialif at least oneInitial, otherwisePending.
Example:
const v1 = initial; // Result<TypeError, number>.Initial
const v2 = pending; // Result<TypeError, number>.Pending
const v3 = success<TypeError, number>(2); // Result<TypeError, number>.Success
const v4 = success<ReferenceError, string>("test"); // Result<ReferenceError, string>.Success
const v5 = failure<Error, boolean>(new Error()); // Result<Error, boolean>.Failure
const r1 = merge([v1, v2]); // Result<TypeError, [number, number]>.Initial
const r2 = merge([v2, v5]); // Result<TypeError | Error, [number, boolean]>.Pending
const r3 = merge([v3, v4]); // Result<TypeError | ReferenceError, [number, string]>.Success
const r4 = merge([v3, v4, v5]); // Result<TypeError | ReferenceError | Error, [number, string, boolean]>.FailuremergeInMany
static mergeInMany<F1, S1>(values: [Result<F1, S1>]): Result<Array<F1>, [S1]>;
static mergeInMany<F1, S1, F2, S2>(values: [Result<F1, S1>, Result<F2, S2>]): Result<Array<F1 | F2>, [S1, S2]>;
static mergeInMany<F1, S1, F2, S2, F3, S3>(
values: [Result<F1, S1>, Result<F2, S2>, Result<F3, S3>]
): Result<Array<F1 | F2 | F3>, [S1, S2, S3]>;
// ... until 10 elementsvalues: Array<Result<F, S>>- Array of Result values which will be merged into Result of Array- Returns
Result<Array<F>, Array<S>>which will containSuccess<Array<S>>if all of array elements wasSuccess<R>otherwise array of all catchedFailure<F>values.
Example:
const v1 = success<TypeError, number>(2); // Result<TypeError, number>.Success
const v2 = failure<ReferenceError, string>("test"); // Result<ReferenceError, string>.Success
const v3 = failure<Error, boolean>(new Error()); // Result<Error, boolean>.Failure
merge([v1, v2]); // Result<Array<TypeError | ReferenceError>, [number, string]>.Success
merge([v1, v2, v3]); // Result<Array<TypeError | ReferenceError | Error>, [number, string, boolean]>.Failureinitial
const initial: Result<never, never>;- Returns
ResultwithInitialstate which does not contain value.
Example:
const v1 = initial; // Result<undefined, never>.Initialpending
const pending: Result<F, S>;- Returns
ResultwithPendingstate which does not contain value.
Example:
const v1 = pending; // Result<never, never>.Initialfailure
function failure<F, S>(value: F): Result<F, S>;- Returns
ResultwithFailurestate which contain value withFtype.
Example:
const v1 = failure(new Error()); // Result<Error, never>.Failure
const v2 = failure<Error, number>(new Error()); // Result<Error, number>.Failuresuccess
function success<F, S>(value: S): Result<F, S>;- Returns
ResultwithSuccessstate which contain value withStype.
Example:
const v1 = success(2); // Result<never, number>.Success
const v2 = success<Error, number>(2); // Result<Error, number>.Successfrom
Alias for success
function from<S>(value: S): Result<never, S>;- Returns
ResultwithSuccessstate which contain value withStype.
Example:
from(2); // Result<never, number>.SuccessfromTry
Returns Success with function result or Failure if function execution throws an error.
function fromTry<L, R>(fn: () => R): Result<L, R>;fromTry(() => 2); // Result<unknown, number>.Success
fromTry(() => {
throw new Error("test");
}); // Result<unknown, never>.FailurefromPromise
Returns promise of Success if the provided promise fulfilled or Failure with the error value if the provided promise rejected.
function fromPromise<R>(promise: Promise<R>): Promise<Result<unknown, R>>;fromPromise(Promise.resolve(2)); // Promise<Result<unknown, number>.Right>
fromPromise(Promise.reject(new Error("test"))); // Promise<Result<unknown, never>.Left>fromMaybe
function fromMaybe<never, S>(value: Maybe<S>): Result<never, S>;- Creates
ResultfromMaybeinInitialorSuccessstate.
Example:
fromMaybe(just(2)); // Result<never, number>.Success
fromMaybe(none()); // Result<never, number>.InitialfromEither
function fromEither<F, S>(value: Either<F, S>): Result<F, S>;- Creates
ResultfromEitherinFailureorSuccessstate.
Example:
fromEither(right<string, number>(10)); // Result<string, number>.SuccessfromNullable
function fromNullable<T>(value: T): Result<unknown, NonNullable<T>>;- Creates
ResultwithSuccessstate which contain value withTtype if value is not null or undefined andinitialotherwise.
Example:
fromNullable(10); // Result<unknown, number>.Success
fromNullable(null as Nullable<number>); // Result<unknown, number>.InitialisResult
function isResult<F, S>(value: unknown | Result<F, S>): value is Result<L, R>;- Returns
booleanif givenvalueis instance of Result constructor.
Example:
const value: unknown = 2;
if (isResult(value)) {
// ... value is Result<unknown, unknown> at this block
}Result#isInitial
function isInitial(): boolean;- Returns
trueif state ofResultisInitialotherwisefalse
Example:
const v1 = success(2);
const v2 = failure(2);
const v3 = initial();
v1.isInitial(); // false
v2.isInitial(); // false
v3.isInitial(); // trueResult#isPending
function isPending(): boolean;- Returns
trueif state ofResultisPendingotherwisefalse
Example:
const v1 = success(2);
const v2 = failure(2);
const v3 = pending();
v1.isPending(); // false
v2.isPending(); // false
v3.isPending(); // trueResult#isFailure
function isFailure(): boolean;- Returns
trueif state ofResultisFailureotherwisefalse
Example:
const v1 = success(2);
const v2 = failure(2);
v1.isFailure(); // false
v2.isFailure(); // trueResult#isSuccess
function isSuccess(): boolean;- Returns
trueif state ofResultisSuccessotherwisefalse
Example:
const v1 = success(2);
const v2 = failure(2);
v1.isSuccess(); // true
v2.isSuccess(); // falseResult#or
function or<F, S>(x: Result<F, S>): Result<F, S>;- Returns
Result<F, S>. If state ofthisisSuccessthenthiswill be returned otherwisexargument will be returned
Example:
const v1 = success<string, number>(2);
const v2 = failure<string, number>("Error 1");
const v3 = failure<string, number>("Error 2");
const v4 = success<string, number>(3);
const v5 = initial();
v1.or(v2); // v1 will be returned
v2.or(v1); // v1 will be returned
v2.or(v3); // v3 will be returned
v1.or(v4); // v1 will be returned
v5.or(v1); // v1 will be returned
v2.or(v3).or(v1); // v1 will be returned
v2.or(v1).or(v3); // v1 will be returned
v1.or(v2).or(v3); // v1 will be returned
v2.or(v5).or(v3); // v3 will be returnedResult#join
function join<L1, L2, R>(this: Result<L1, Result<L2, R>>): Result<L1 | L2, R>;this: Result<F1, Result<F2, S>>-Resultinstance which contains otherResultinstance asSuccessvalue.- Returns unwrapped
Result- if currentResulthasSuccessstate and innerResulthasSuccessstate then returns innerResultSuccess, if innerResulthasFailurestate then return innerResultFailureotherwise outerResultFailure.
Example:
const v1 = success(success(2));
const v2 = success(failure(new Error()));
const v3 = failure<TypeError, Result<Error, number>>(new TypeError());
v1.join(); // Result.Success with value 2
v2.join(); // Result.Failure with value new Error
v3.join(); // Result.Failure with value new TypeErrorResult#map
Alias for Result#mapSuccess
function map<F, S, NewS>(fn: (val: S) => NewS): Result<F, NewS>;- Returns mapped by
fnfunction value wrapped byResultifResultisSuccessotherwiseResultwith current value
Example:
const v1 = success<Error, number>(2);
const v2 = failure<Error, number>(new Error());
const newVal1 = v1.map(a => a.toString()); // Result<Error, string>.Success with value "2"
const newVal2 = v2.map(a => a.toString()); // Result<Error, string>.Failure with value new Error()Result#mapSuccess
function mapSuccess<F, S, NewS>(fn: (val: S) => NewS): Result<F, NewS>;- Returns mapped by
fnfunction value wrapped byResultifResultisSuccessotherwiseResultwith current value
Example:
const v1 = success<Error, number>(2);
const v2 = failure<Error, number>(new Error());
const newVal1 = v1.mapSuccess(a => a.toString()); // Result<Error, string>.Success with value "2"
const newVal2 = v2.mapSuccess(a => a.toString()); // Result<Error, string>.Failure with value new Error()Result#mapLeft
function mapFailure<F, S, NewF>(fn: (val: F) => NewF): Result<NewF, S>;- Returns mapped by
fnfunction value wrapped byResultifResultisFailureotherwiseResultwith current value
Example:
const v1 = success<Error, number>(2);
const v2 = failure<Error, number>(new Error());
const newVal1 = v1.mapFailure(a => a.toString()); // Result<string, number>.Right with value 2
const newVal2 = v2.mapFailure(a => a.toString()); // Result<string, number>.Left with value "Error"Result#asyncMap
function asyncMap<F, S, NewS>(fn: (val: S) => Promise<NewS>): Promise<Result<F, NewS>>;- Returns
Promisewith mapped byfnfunction value wrapped byResultifResultisSuccessotherwiseResultwith current value
Example:
const v1 = success<Error, number>(2);
const v2 = failure<Error, number>(new Error());
// Promise<Result<Error, string>.Success> with value "2"
const newVal1 = v1.asyncMap(a => Promise.resolve(a.toString()));
// Promise<Result<Error, string>.Failure> with value new Error()
const newVal2 = v2.asyncMap(a => Promise.resolve(a.toString()));Result#apply
function apply<A, B>(this: Result<L, (a: A) => B>, arg: Result<L, A>): Result<L, B>;
function apply<A, B>(this: Result<L, A>, fn: Result<L, (a: A) => B>): Result<L, B>;this | fn- function wrapped by Result, which should be applied to valueargarg | this- value which should be applied tofn- Returns mapped by
fnfunction value wrapped byResultifResultisSuccessotherwiseResultwith current value
Example:
const v1 = success<Error, number>(2);
const v2 = failure<Error, number>(new Error());
const fn1 = success<Error, (a: number) => number>((a: number) => a * 2);
const fn2 = failure<Error, (a: number) => number>(new Error());
const newVal1 = fn1.apply(v1); // Result<Error, number>.Right with value 4
const newVal2 = fn1.apply(v2); // Result<Error, number>.Left with value new Error()
const newVal3 = fn2.apply(v1); // Result<Error, number>.Left with value new Error()
const newVal4 = fn2.apply(v2); // Result<Error, number>.Left with value new Error()Result#asyncApply
Async variant of Result#apply
asyncApply<A, B>(
this: Result<F, (a: Promise<A> | A) => Promise<B>>,
arg: Result<F, Promise<A>>): Promise<Result<F, B>>;
asyncApply<A, B>(
this: Result<F, Promise<A>>,
fn: Result<F, Promise<(a: Promise<A> | A) => B>>): Promise<Result<F, B>>;
asyncApply<A, B>(
this: Result<F, Promise<A>> | Result<F, (a: Promise<A> | A) => Promise<B>>,
argOrFn: Result<F, Promise<A>> | Result<F, (a: Promise<A> | A) => Promise<B>>): Promise<Result<F, B>>this | fn- function wrapped by Result, which should be applied to valueargarg | this- value which should be applied tofn- Returns
Promisewith mapped byfnfunction value wrapped byResultifResultisSuccessotherwiseResultwith current value
Example:
const v1 = success<Error, number>(2);
const v2 = failure<Error, number>(new Error());
const fn1 = success<Error, (a: number) => Promise<number>>((a: number) => Promise.resolve(a * 2));
const fn2 = failure<Error, (a: number) => Promise<number>>(new Error());
const newVal1 = fn1.asyncApply(v1); // Promise<Either<Error, number>.Right> with value 4
const newVal2 = fn1.asyncApply(v2); // Promise<Either<Error, number>.Left> with value new Error()
const newVal3 = fn2.asyncApply(v1); // Promise<Either<Error, number>.Left> with value new Error()
const newVal4 = fn2.asyncApply(v2); // Promise<Either<Error, number>.Left> with value new Error()Result#chain
function chain<F, S, NewF, NewS>(fn: (val: S) => Either<NewF, NewS>): Either<F | NewF, NewS>;- Returns mapped by
fnfunction value wrapped byResultifResultisSuccessand returned byfnvalue isSuccesstoo otherwiseResultin other state,InitialpwnsPendingandFailure.
Example:
const v1 = success<Error, number>(2);
const v2 = failure<Error, number>(new Error());
const v3 = initial;
// Result<Error | TypeError, string>.Success with value "2"
const newVal1 = v1.chain(a => success<TypeError, string>(a.toString()));
// Result<Error | TypeError, string>.Failure with value new TypeError()
const newVal2 = v1.chain(a => failure<TypeError, string>(new TypeError()));
// Result<Error | TypeError, string>.Failure with value new Error()
const newVal3 = v2.chain(a => success<TypeError, string>(a.toString()));
// Result<Error | TypeError, string>.Failure with value new Error()
const newVal4 = v2.chain(a => failure<TypeError, string>(new TypeError()));
// Result<TypeError, string>.Initial with no value
const newVal5 = v3.chain(a => failure<TypeError, string>(new TypeError()));Result#asyncChain
function asyncChain<F, S, NewF, NewS>(fn: (val: S) => Promise<Result<NewF, NewS>>): Promise<Result<F | NewF, NewS>>;- Returns
Promisewith mapped byfnfunction value wrapped byResultifResultisSuccessand returned byfnvalue isSuccesstoo otherwiseResultin other state,InitialpwnsPendingandFailure.
Example:
const v1 = success<Error, number>(2);
const v2 = failure<Error, number>(new Error());
const v3 = initial;
// Promise<Result<Error | TypeError, string>.Success> with value "2"
const newVal1 = v1.asyncChain(a => Promise.resolve(right<TypeError, string>(a.toString())));
// Promise<Result<Error | TypeError, string>.Failure> with value new TypeError()
const newVal2 = v1.asyncChain(a => Promise.resolve(left<TypeError, string>(new TypeError()));
// Promise<Result<Error | TypeError, string>.Failure> with value new Error()
const newVal3 = v2.asyncChain(a => Promise.resolve(right<TypeError, string>(a.toString())));
// Promise<Result<Error | TypeError, string>.Failure> with value new Error()
const newVal4 = v2.asyncChain(a => Promise.resolve(left<TypeError, string>(new TypeError())));
// Promise<Result<Error | TypeError, string>.Initial> with no value
const newVal5 = v3.asyncChain(a => Promise.resolve(failure<TypeError, string>(new TypeError())));Result#toEither
function toEither<F, S>(onInitial: () => F, onPending: () => F): Either<F, S>;- Converts
ResultintoEitherinLeftorSuccessstate with fallbacks forInitialandPendingstates.
Example:
success<string, number>(10).toEither(
() => "initial state",
() => "pending state"
); // Either<string, number>.RightResult#toMaybe
function toMaybe<S>(): Maybe<S>;- Converts
ResultintoMaybeinJuststate ifResultis inSuccessstate or toMaybeinNonestate otherwise.
Example:
success<string, number>(10).toMaybe(); // Maybe<number>.JustResult#toNullable
function toNullable<S>(): S | null;- Returns S if
Resultis inSuccessstate and null otherwise
Example:
success<string, number>(10).toNullable(); // number | nullResult#toUndefined
function toUndefined<S>(): S | undefined;- Returns S if
Resultis inSuccessstate and undefined otherwise
Example:
success<string, number>(10).toUndefined(); // number | undefinedResult#unwrap
function unwrap<S>(): S;- Returns S if
Resultis inSuccessstate and throws otherwise via provided factory or pure Error
Example:
success<string, number>(10).unwrap(); // number
initial.unwrap(); // throws default (Error)
pending.unwrap({ failure: () => new Error('Custom')}); // throws custom (Error)Result#fold
function fold<D>(onInitial: () => D, onPending: () => D, onFailure: (failure: F) => D, onSuccess: (success: S) => D): S;- Extracts value from
Resultand converts it toDbased on the factory
Example:
const onInitial = () => "it's initial"
const onPending = () => "it's pending"
const onFailure = (err) => "it's failure"
const onSuccess = (data) => `${data + 1}`
const f = fold(onInitial, onPending, onFailure, onSuccess)
f(initial()) // "it's initial"
f(pending()) // "it's pending"
f(failure(new Error('error text'))) // "it's failure"
f(success(21)) // '22'Helpers
// Value from Result instance
const { value } = success<Error, number>(2); // number | Error | undefined
const { value } = success(2); // number | undefined
const { value } = failure<Error, number>(new Error()); // number | Error | undefined
const { value } = failure(new Error()); // Error | undefinedsuccess(2).unwrap() // number
failure(new TypeError()).unwrap() // throws
failure(2).unwrap() // throws (don't do this)
failure(2).unwrapOr(3) // returns 3
success(2).unwrapOr(3) // returns 2
failure(2).unwrapOrElse(num => num * 2) // returns 4
success(2).unwrapOrElse(num => num * 2) // returns 2Development
License
MIT (c)