supermacro/neverthrow

I know how to keep result value type when inject other deal in orElse().

mitsuru793 opened this issue · 1 comments

Thank you for maintaining this library.

I know how to keep result value type when inject other deal.
Don't orElse() return other type error?

const writeCache = (cacheFilePath: string, content: string): Result<void, Error> => {
    fs.writeFileSync(cacheFilePath, content)
    return ok(undefined)
}

const requestByApi = async (): Promise<string> => {
    console.log('calling api...')
    return Promise.resolve('new content')
}

const safeRequestByApi = (): ResultAsync<string, unknown> => {
    return ResultAsync.fromPromise(requestByApi(), (e) => e)
}

okAsync(undefined)
    .orElse(_ => {  // Compile Error
        return safeRequestByApi()
            .andThen(res => {
                const mayWrote = writeCache('cachePath', res) // I want to tap only.
                if (mayWrote.isOk()) {
                    return ok(res) // I want not change return keeping the returned of safeRequestByApi().
                }
                return err(mayWrote.error)
            })
    })

Got TypeScript Error

No overload matches this call.
  Overload 1 of 3, '(f: (e: never) => Result<undefined, unknown>): ResultAsync<undefined, unknown>', gave the following error.
    Argument of type '(_: never) => ResultAsync<string, unknown>' is not assignable to parameter of type '(e: never) => Result<undefined, unknown>'.
      Type 'ResultAsync<string, unknown>' is not assignable to type 'Result<undefined, unknown>'.
        Type 'ResultAsync<string, unknown>' is missing the following properties from type 'Err<undefined, unknown>': error, isOk, isErr, asyncAndThen, and 3 more.
  Overload 2 of 3, '(f: (e: never) => ResultAsync<undefined, unknown>): ResultAsync<undefined, unknown>', gave the following error.
    Argument of type '(_: never) => ResultAsync<string, unknown>' is not assignable to parameter of type '(e: never) => ResultAsync<undefined, unknown>'.
      Type 'ResultAsync<string, unknown>' is not assignable to type 'ResultAsync<undefined, unknown>'.
        Type 'string' is not assignable to type 'undefined'.
  Overload 3 of 3, '(f: (e: never) => Result<undefined, unknown> | ResultAsync<undefined, unknown>): ResultAsync<undefined, unknown>', gave the following error.
    Argument of type '(_: never) => ResultAsync<string, unknown>' is not assignable to parameter of type '(e: never) => Result<undefined, unknown> | ResultAsync<undefined, unknown>'.
      Type 'ResultAsync<string, unknown>' is not assignable to type 'Result<undefined, unknown> | ResultAsync<undefined, unknown>'.
        Type 'ResultAsync<string, unknown>' is not assignable to type 'ResultAsync<undefined, unknown>'.ts(2769)

when inject other deal.

I'm not sure what you mean by this.


In any case, I am able to reproduce your type error. But this stems from a mismatch of types and not some bug in neverthrow.

Looking at the type signature of orElse

class Result<T, E> { // <--- INPUT
  orElse<A>(
    callback: (error: E) => Result<T, A>
  ): Result<T, A> { ... } // <-- OUTPUT
}

We can see that the input result has a "success" type of T and the output result has a success type of T. In other words; the success type does not change.

You can fix this by changing the code to the following:

  const writeCache = (cacheFilePath: string, content: string): Result<void, Error> => {
    fs.writeFileSync(cacheFilePath, content)
    return ok(undefined)
  }

  const requestByApi = async (): Promise<string> => {
    console.log('calling api...')
    return Promise.resolve('new content')
  }

  const safeRequestByApi = (): ResultAsync<string, unknown> => {
    return ResultAsync.fromPromise(requestByApi(), (e) => e)
  }

+ okAsync<string, unknown>('now its a string')
- okAsync(undefined)
    .orElse(_ => {  // Compile Error
        return safeRequestByApi()
            .andThen(res => {
                const mayWrote = writeCache('cachePath', res) // I want to tap only.
                if (mayWrote.isOk()) {
                    return ok(res) // I want not change return keeping the returned of safeRequestByApi().
                }
                return err(mayWrote.error)
            })
    })