Skip to content
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

Fix not being able to call Result's or and orElse methods #188

Merged
merged 4 commits into from
Feb 6, 2025

Conversation

jstasiak
Copy link
Collaborator

@jstasiak jstasiak commented Feb 5, 2025

The error solved here:

test/result.test.ts:327:38 - error TS2349: This expression is not callable.
  Each member of the union type '(<R extends Result<number, unknown>>(_other: (error: never) => R) => OkImpl<number>) | (<R extends Result<unknown, unknown>>(other: (error: string) => R) => R)' has signatures, but none of those signatures are compatible with each other.

327     const resultAfterOrElse = result.orElse((error) => Err(error))
                                         ~~~~~~
test/result.test.ts:327:46 - error TS7006: Parameter 'error' implicitly has an 'any' type.

327     const resultAfterOrElse = result.orElse((error) => Err(error))
                                                 ~~~~~

The same issue existed in both implementations and the fix is basically the same.

There are two aspects of this patch, one I'm pretty confident about and the other remains an open question:

  1. Calling or() and orElse() should compile and work at runtime - this is prtty clear and it works.
  2. What should the type be if we have enough information to help narrow the types?

To expand on the latter point, for example let's say we have

const result: Result<number, string> = ...
const final = result.or(Ok(1))

then the type of final could be, in principle, one of these:

a. Result<number, string>
b. Result<number, never>
c. Ok

We know it's going to be Ok but I'm not entirely sure it should be reflected in the type because I can totally see this kind of type narrowing resulting in losing precious type information in the client code where someone would like to maintain a Result<T, E> context and not have it narrowed down to Ok.

I'm not very confident about that but that's my prediction.

Therefore I went with that expression evaluating to option b (Result<number, never>) to at least remain in the Result context and the implementation of this is simple enough.

Also I feel that the case where the "other" result is gonna be statically known to always be Ok is going to be an edge case we don't have to worry about.

If this proves to be a problem let's revisit the issue.

Resolves: #175

The error solved here:

    test/result.test.ts:327:38 - error TS2349: This expression is not callable.
      Each member of the union type '(<R extends Result<number, unknown>>(_other: (error: never) => R) => OkImpl<number>) | (<R extends Result<unknown, unknown>>(other: (error: string) => R) => R)' has signatures, but none of those signatures are compatible with each other.

    327     const resultAfterOrElse = result.orElse((error) => Err(error))
                                             ~~~~~~
    test/result.test.ts:327:46 - error TS7006: Parameter 'error' implicitly has an 'any' type.

    327     const resultAfterOrElse = result.orElse((error) => Err(error))
                                                     ~~~~~

The same issue existed in both implementations and the fix is basically
the same.

There are two aspects of this patch, one I'm pretty confident about and
the other remains an open question:

1. Calling or() and orElse() should compile and work at runtime - this
   is prtty clear and it works.
2. What should the type be if we have enough information to help narrow
   the types?

To expand on the latter point, for example let's say we have

    const result: Result<number, string> = ...
    const final = result.or(Ok(1))

then the type of final could be, in principle, one of these:

a. Result<number, string>
b. Result<number, never>
c. Ok<number>

We know it's going to be Ok<number> but I'm not entirely sure it should
be reflected in the type because I can totally see this kind of type
narrowing resulting in losing precious type information in the client
code where someone would like to maintain a Result<T, E> context and not
have it narrowed down to Ok<T>.

I'm not very confident about that but that's my prediction.

Therefore I went with that expression evaluating to option b
(Result<number, never>) to at least remain in the Result context and the
implementation of this is simple enough.

Also I feel that the case where the "other" result is gonna be
statically known to always be Ok is going to be an edge case we don't
have to worry about.

If this proves to be a problem let's revisit the issue.

Resolves: #175
Copy link

@raguiar9080 raguiar9080 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the Result<number, never> final type, feels the most appropriate 👍

@jstasiak
Copy link
Collaborator Author

jstasiak commented Feb 6, 2025

Yeah that's my thinking as well, actually using this API will tell us if we're right.

@jstasiak jstasiak enabled auto-merge (squash) February 6, 2025 10:46
@jstasiak jstasiak merged commit a796eb1 into master Feb 6, 2025
1 of 2 checks passed
@jstasiak jstasiak deleted the fix-or-methods branch February 6, 2025 10:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Result.orElse is broken ("This expression is not callable")
2 participants