mobx-state-tree: [TypeScript 3.6+] flow() | Inferred Generator type is not assignable to IterableIterator
Feature request
Is your feature request related to a problem? Please describe.
Prior to TS 3.6 flow() yield return types would be typed as any due to typescript limitations. Those limitations are being addressed in TS 3.6 release as described in this iteration plan
Describe the solution you’d like
Full support for yield return types in flow()
Describe alternatives you’ve considered
If the full inference is not possible due to some TS limitations (I haven’t completely dived into new generators typings) I wouldn’t mind having to manually declare generic parameters in flow()
Additional context I’ve seen some mentions in this PR, but I have not seen the actual issue for tracking this particular case
Are you willing to (attempt) a PR?
- Yes
- No
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 10
- Comments: 22 (13 by maintainers)
Commits related to this issue
- [minor] Merge pull request #1409 from nulladdict/flow-inference [Typescript] Improving flow typings #1378 — committed to mobxjs/mobx-state-tree by mweststrate 5 years ago
Sorry for slacking on this. For now I’ve come up with this temporary solution:
Good parts:
GeneratortypePromiseto be yielded, which is required byflow()but wasn’t type checked beforeBad part is that whatever TS infers has to be compatible with every single one of the yield calls. In practice it turns into something like this:
The return type is inferred correctly, but type for
x,yandzisnumber | string | boolean.Bottom line is, in case of having only one yield call TS works as expected, otherwise we get a union type, which is in my opinion still better than having
any, since it’s at least partially type checks.v3.15.0 released, please give it a try 😃
How to use it:
The only thing that is still not quite working is getting the proper return type from yields, so those will still return any for now and should be type casted. e.g.
actionAsync is an alternative to mobx flows, not mobx state tree ones. To make them work with mobx state tree it would need its own particular implementation (same than there’s a different implementation for flows in mobx).
That being said I’ll cut a new version of mst with the fix for flow typings, but it will make ts 3.6 the new minimum version when using flows
@xaviergonz I’m now able to remove the icky typing and I can confirm it works. Thank you!
you guys should check out
mobx-utilsactionAsync thanks to the great work of @xaviergonz! I could rewrite all of my flows using that pretty easily.As for
flow()post TS3.6, I think instead ofGenerator<Promise<any>, void, any>I would useGenerator<Promise<unknown>, void, unknown>so that Typescript will force me to cast type.Upon further research, new TS typings are indeed not flexible enough to provide strongly typed
next(), since it would require to define relationships betweenyield’ed invocations, which is not possible (at least as of yet).However, as @fruitraccoon mentioned we now get compile errors, since the new inferred
Generator<…>type is not assignable toIterableIterator<…>, because of the ability to distinguish betweenyieldandreturntypes.I believe this issue should be about compatibility with TS 3.6+, but I am not quite sure that there’s a backward-compatible change to be made, so that’s a real problem
I see that the linked mobx issue mentioned above has been closed as the TS typing was deemed not powerful enough. However, I get compile errors when attempting to use TS 3.6.2 with MST flow because of the new types.
The code below shows me a typescript error (sorry, I couldn’t find anywhere online to create a demo):
The error itself is:
The best workaround that I’ve found looks like this:
This explicitly types the returned generator. The
anyas the third generic parameter fixes/hides the issue.Perhaps there’s a way to update
flowso that this explicit typing isn’t required? (ideally in a backward-compatible way)I would argue, that falling back to
Promise<any>is a shaky solution. It is quite unsound, so you might forget to specify a type or specify a wrong type and TS would not complain about it.Inferred union type is more verbose(you’re forced to use “useless” type guard or
ascast), but it is also more sound. Consider this: (playground link)In this example
ascast is pretty safe, because starting at some version TS would complain about casting non-overlapping types.Downside is you can still choose an incorrect type from a union, and TS would fail to notice: (playground link)
For my case it is a trade-off I’m willing to take in favour of having
any