-
Notifications
You must be signed in to change notification settings - Fork 86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
.match with functions that return Result<., .> #594
Comments
The pattern I have adopted in the meantime is: fromSafePromise(x.match(asyncFn1, asyncFn2).then(id)).andThen(id) Where |
Hi @timvandam, I’m having a hard time understanding what you want to do. Could you provide a more complete example ? |
What I’m trying to achieve is behavior analogous to Promise.then with 2 arguments: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#onrejected. This is not the same as chaining .then and .catch (and similarly .andThen and .orElse can nit be applied to achieve the same behavior in a simple way) You provide two functions that handle the ok/err cases and return a (Async)Result |
Thanks @timvandam. Reading this also helped me grasp the topic. Wouldn't the best solution be to mimick |
@paduc Yes that would be fine too. I personally would prefer |
I like this because it kind of completes the "toolbox". For mapping to a result:
For mapping to a value/err:
On the other hand, can you already accomplish the same thing by just returning a ResultAsync from both branches of your match? |
@macksal achieving the behavior of resultAsync
.andThen(async (val) => {
if (await condition(val)) {
return ok(val);
}
return err({ passthrough: true, error: new Error('condition not met') });
})
.orElse(async (val) => {
if ('passthrough' in val && val.passthrough) return err(val.err);
// normal error handling logic here
}) The difference between Edit: I misread your question - you are talking about match. This is not possible as neverthrow/src/result-async.ts Lines 186 to 188 in ac52282
|
@timvandam understood. It works normally for sync but not for async. |
In my opinion, it's valuable to have an API with a clear distinction between the Result/ResultAsync "world" (eg andThen, orElse, map, mapErr, ... all methods with a When there is a back and forth between these worlds, it starts to smell like bad code. I'm all for adding methods like |
Just stumbled upon this conversation, when I wanted to create a new issue about the same topic. I just started playing around with A good example is the I already fiddled around with it, and I think it should be feasible, without adding breaking changes and without loosing type-safety. Example: export class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
orElse<R extends Result<unknown, unknown>>(
f: (e: E) => R,
): ResultAsync<InferOkTypes<R> | T, InferErrTypes<R>>
orElse<R extends ResultAsync<unknown, unknown>>(
f: (e: E) => R,
): ResultAsync<InferAsyncOkTypes<R> | T, InferAsyncErrTypes<R>>
orElse<U, A>(f: (e: E) => Result<U, A> | ResultAsync<U, A>): ResultAsync<U | T, A>
orElse<R>(f: (e: E) => R): ResultAsync<R | T, E> // added this one
orElse<R>(
f: (e: E) => Result<R, unknown> | ResultAsync<R, unknown> | R,
): ResultAsync<unknown, unknown> {
// ...
}
} Example of a type-guard that can be used under the hood: export function isResult<T, E>(result: Result<T, E> | unknown): result is Result<T, E> {
return result instanceof Ok || result instanceof Err
} Example of a normalize function that can be used under the hood: export function normalize<T, E>(value: ResultAsync<T, E> | Result<T, E> | T): ResultAsync<T, E> {
return value instanceof ResultAsync
? value
: new ResultAsync<T, E>(
Promise.resolve<Result<T, E> | T>(value).then((it) => (isResult(it) ? it : ok(it))),
)
} Is this an idea that could be considered? I'd be happy to submit a PR for this. |
This is how I normalize: const myResultAsync = okAsync().andThen(() => myUnknownResult); |
Well, my problem is not really that it's not possible with the current API, but it's just a bit cumbersome. I am trying to combine some generic utils (that are not coupled to myResultAsync
.orElse(ifInstanceOf(ProblemError, convertProblemError))
.orElse(ifInstanceOf(HttpStatusError, convertHttpStatusError))
.orElse(ifInstanceOf(MyCustomError, convertMyCustomError))
.unwrapOr(response({ message: 'Internal server error' }, { statusCode: 500 })); instead, I need to write: myResultAsync
.orElse(fromThrowable(ifInstanceOf(ProblemError, convertProblemError)))
.orElse(fromThrowable(ifInstanceOf(HttpStatusError, convertHttpStatusError)))
.orElse(fromThrowable(ifInstanceOf(MyCustomError, convertMyCustomError)))
.unwrapOr(response({ message: 'Internal server error' }, { statusCode: 500 })); (for context, my function ifInstanceOf<TError, TResult>(errorClass: Class<TError>, errorConverter: (error: TError) => TResult) {
return (e: unknown) => {
if (e instanceof errorClass) {
return errorConverter(e);
}
throw e;
};
} Which seems a bit of a missed opportunity. Of course I can just use a plain Promise for this: return myPromise
.catch(ifInstanceOf(ProblemError, convertProblemError))
.catch(ifInstanceOf(HttpStatusError, convertHttpStatusError))
.catch(ifInstanceOf(MyCustomError, convertMyCustomError))
.catch(() => response({ message: 'Internal server error' }, { statusCode: 500 })); but I would really like better interoperability with |
Currently it does not seem possible to handle both
Ok
andErr
at the same time. Note that this behavior is different from.andThen(...).orElse(...)
or.orElse(...).andThen(...)
as the first of the two chained functions may have its return value handled by the second of the two. As far as I am aware there is no neat way of handling this currently..match
comes close, but returns aPromise
rather thanResultAsync
when applied toResultAsync
.I propose adding the following:
I am willing to submit a PR if this idea is accepted
The text was updated successfully, but these errors were encountered: