3 ms·
I think the comparison in the redux example really isn't fair. He defines just one enumeration in ReasonML and uses many different constants and interfaces in T
by lovasoa 7y ago
I think the comparison in the redux example really isn't fair. He defines just one enumeration in ReasonML and uses many different constants and interfaces in Typescript.
The equivalent of
type action =
| AddMovie(string)
| RemoveMovie(string)
| Reset
in Typescript is more something like :
type Action =
| { type: "ADD_MOVIE", movie: string }
| { type: "REMOVE_MOVIE", movie: string }
| { type: "RESET"};
- nirvdrum 7y agoSince TS is just JS, you could just dispatch to arbitrary strings, but the TS type system isn't going to help you out very much. The approach the author is taking is the one described in the Redux documentation [0]. [0] -- https://redux.js.org/recipes/usage-with-typescript https://redux.js.org/recipes/usage-with-typescript
- acemarke 7y agoHi, I'm a Redux maintainer. FWIW, there's apparently a ton of ways to handle typing Redux with TS (and that's even reflected in the discussion for the PR that added that page [0]). A large part of it seems to revolve around whether or not you're trying to apply action types to `dispatch` to limit what actions can be dispatched. I'm comfortable with TS at a general level (declaring interfaces for API types, reducers, and React component props), but a lot of folks seem to go crazy trying to get 100% type coverage. I personally aim for more of an "80%" sweet spot for typing - trying to trade off overall benefit with not trying to tie myself in knots adding ridiculous type declarations. I actually just wrote a tutorial page for Redux Starter Kit that shows how to use it with TS and `useDispatch` [1], but had someone point out that one of the examples I was using for thunks was losing a bit of type safety [2]. This stuff is hard :) [0] https://github.com/reduxjs/redux/pull/3201 https://github.com/reduxjs/redux/pull/3201 [1] https://redux-starter-kit.js.org/tutorials/advanced-tutorial https://redux-starter-kit.js.org/tutorials/advanced-tutorial [2] https://github.com/markerikson/rsk-github-issues-example/pull/1 https://github.com/markerikson/rsk-github-issues-example/pul...
- nirvdrum 7y agoThanks for the feedback. The earliest examples I had see of using redux just passed strings around and struck me as being error-prone. So I was happy to see const fields used as the action types. The TypeScript approach looks reasonable to me, if not verbose. But it looks like there's no real consensus on what the best practice is. Certainly some food for thought. Thanks!
- crimsonalucard 7y agoRight but the type script version isn't type checked. You need explicitly check for those strings at runtime. The type checker needs to prove the program type correct without explicit input from you. ReasonML has better theoretical foundation. The variant type is a primitive type that TS and many other languages miss.
- spion 7y agoThe type script version is type checked: http://bit.ly/2lVoSgY http://bit.ly/2lVoSgY type State = { movies: string[] }; type Action = | {type: 'AddMovie', movie: string} | {type: 'RemoveMovie', movie: string} | {type: 'Reset'} let defaultState:State = { movies: [] } let reducer = (state: State, action: Action): State => { switch(action.type) { case 'AddMovie': return { movies: [action.movie, ...state.movies] } case 'RemoveMovie': return { movies: state.movies.filter(m => m !== action.movie) } case 'Reset': return defaultState; } } /* No need for additional functions! */ let someAction = {type: 'AddMovie' as const, movie: "The End of Evangelion"} reducer(defaultState, someAction); The secret is that typescript doesn't actually lack variants - it does have the equivalent of polymorphic variants. Try adding a new action type, you will get a type error for the reducer return type. It won't be a pretty type error, but you can't forget to add a switch case.
- crimsonalucard 7y agoMy mistake, good to know. The syntax really looks like string comparison rather then actual pattern matching.
- spion 7y agoIt is string comparison, although in terms of performance in JS string constant comparison is done by reference so its much faster. I believe this is closer to polymorphic variants than to actual sum types (global namespace like strings, comparison by name). You can tighten it a bit by switching to symbols instead of strings, which would guarantee unique tags, getting closer to classic variants. However TypeScript isn't big on nominal types, and for a good reason: the node module system for example can easily cause two versions of a module to be instantiated in the same program which in turn would cause two different and incompatible instances of the symbols and symbol types. Nevertheless I would still say that FP-like patterns are supported reasonably well.