Why can't I return different error types in a "andThen" chain?
See original GitHub issueHi,
Thank you for publishing this!
I wanted to get started right away but ran into a seemingly trivial problem I am too much of a noob to solve. Any help would be appreciated!
The compiler complains when I want to compose multiple results with the .andThen() method
type E1 = 'invalid'
type E2 = 'also invalid'
type E3 = 'invalid again'
type Errors = E1 | E2 | E3
const f1 = (sth: unknown) => Result<string, E1>
const f2 = (input: string) => Result<string, E2>
const f3 = (thing: string) => Result<string, E3>
const result: Result<string, Errors> = f1('test')
.andThen(f2)
.andThen(f3)
The compiler complains that that the result of f1 is not assignable to the result of f2, specifically Result<string, E1> is not assignable to Result<string, E2>.
I thought that the mismatch of results and function parameters wouldn’t hurt, because .andThen() doesn’t even call the next function if the result is of type Err. Any idea how I could help with the type inference?
Issue Analytics
- State:
- Created 3 years ago
- Comments:20 (16 by maintainers)
Top Results From Across the Web
Catching and handling different types of errors in promise chains
If second (and final) call returns successfully, we'll display a success message. Here's the main promise-chain:
Read more >Error handling in long Promise chains | by Arthur Xavier
The handleError function returned a function which handled the error for that specific step on the Promise chain. It then would set a...
Read more >How to handle error properly in Promise chain? - Stack Overflow
Yes, you want to handle all errors in the promise chain with a single catch. If you need to know which one failed,...
Read more >Multiple error types - Rust By Example
In the following code, two instances of unwrap generate different error types. Vec::first returns an Option , while parse::<i32> returns a Result<i32, ...
Read more >Error handling with promises - The Modern JavaScript Tutorial
Promise chains are great at error handling. When a promise rejects, the control jumps to the closest rejection handler. That's very convenient ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Alright folks … After 8 months of thinking through this, I think I am finally persuaded to go down the route of joining types.
I’m planning on doing some work soonish on this. Maybe I’ll get around to it this week.
I realized that because Rust and Haskell and basically all the other well-known languages that have a
Result
don’t have subtyping. So it’s literally impossible to doT | A
… it’s just not part of those languages. But in typescript, this is allowed!Hi there, I’ve been following this discussion for quite a while now from the sidelines and I do understand @supermacro 's reservations (to some extend)
This is definetely an idioamtic approach in most circumstances, and you are right that
andThen
orbind
or however the equivalent is called in other languages doesn’t allow a different error type to be returned (AFAIK).BUT this approach comes with a cost (and is also sometimes criticized in the respective communities): composability on the macro level can only be achieved by sacrifizing type expressiveness at the mirco level. @supermacro has demonstrated this in one of his previous examples pretty well. Compare: (modified from the OP)
with the proposed solution:
I left out the dicrimininator but I think my argument still stands: In order to compose arbitrary functions returning
Result
types, one has to alter or overload their signatures. Thus reducing the expressiveness of the signature.Additionally it is not possible to centrally handle
Result
types at the moment, because the error types of a given pipeline never line up.I was drawn to this GREAT library, because it allows me to be expressive with error paths. At the same time, the possibility to easily compose different functions without them having to know the context in which they are composed would be a really BIG WIN. Especially, since the propsed changes by @paduc are minimal and non-breaking, I would be in favour of changing the interface.