neverthrow
neverthrow copied to clipboard
feat(andFinally): add andFinallyFunctionality
Description
Add 'finally' functionality to neverthrow. Overall leads to clearer typing with less verbose code. The mental model is similar to the native finally.
This PR attempts to solve 2 main issues. Consider the example below:
const doesResourceExist = async (): Promise<true> => {
const simulateError = () => Math.random() < 0.33
if (simulateError()) {
throw new Error('Unrecoverable error')
}
if (simulateError()) {
throw new Error('404 Not found error')
}
return true
}
const safeFunction: ResultAsync<true, ResultAsync<boolean, unknown>> = ResultAsync.fromPromise(
doesResourceExist(),
(error) => error,
).mapErr((error) => {
if (`${error}` === '404 Not found error') {
return okAsync(false)
}
return errAsync(error)
})
Notice the return type of the safeFunction is typed to an unintuitive type. Not sure if there was a convenient way for error recovery while mutating the Ok type. With andFinally, there is easier control over the Ok type during error recovery as such:
const improvedSafeFunction: ResultAsync<boolean, unknown> = ResultAsync.fromPromise(
doesResourceExist(),
(error) => error,
).andFinally((error) => {
if (`${error}` === 'Not found error') {
return okAsync(false)
}
return errAsync(error)
})
Additionally, this solves #525. As documented in the issue, the below code becomes less verbose.
Before
const doStuff: Result<string, never> = ok('start db connection')
.andThen(() => {
// do stuff that might return an err()
const result: Result<string, string> = ok('do something')
return result
})
.andThen(() => {
return ok('close db connection')
})
.orElse(() => {
return ok('close db connection')
})
After
const doStuff: Result<string, never> = ok("start db connection").andThen(() => {
// do stuff that might return an err()
const result: Result<string, string> = ok("do something")
return result
}).andFinally(() => {
return ok("close db connection")
})
Testing
Have added some minimal tests to capture correct behaviour.