neverthrow icon indicating copy to clipboard operation
neverthrow copied to clipboard

feat(andFinally): add andFinallyFunctionality

Open kishore03109 opened this issue 1 year ago • 0 comments

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.

kishore03109 avatar May 12 '24 16:05 kishore03109