Typescript, Redux, and Immutable.js work together as a pretty compelling stack, but they involve a fair bit of boilerplate compared to Elm or ReasonML.
Elm provides type safety, immutable data structures, and state management in a very compact but readable way. In fact, Redux was strongly inspired by Elm's state management.
All of these stacks are perfectly acceptable choices, so it comes down to a matter of personal preference. I'd encourage everyone to spend some time with Elm and ReasonML. I find them to be productive and very enjoyable tools.
PureScript deserves a mention in this list as well. More full-featured and general purpose than Elm, cleaner design than ReasonML. Prolly the smallest community of the 3.
I played with PureScript a little. It has advanced type system that captures a lot of semantics of JS allowing easy interobility with JS ecosystem. But that comes with a huge price. With all those monads and monad transformers interacting with JS it is easy to end up with stateful mess. Surely, it will be typed and functional mess, but still it is hard to maintain with all state pieces spread through code hiding in lambdas.
With Elm the situation is very different. All your code is pure. The runtime hides the state management and the only way to interact with JS is via message passing. The end result is code that is extremely easy to follow, maintain and debug. Moreover, as Elm style and tooling discourages using lambda-style callbacks with opaque state, all state is very explicit and often just by looking at the state data structures, one often can grasp what the code is doing. It is almost like code follows from the data structures.
Elm is a lang and a FW in one. Very opinionated. Very batteries included.
PS is a lang. It compiles well to JS, but there is no friction against making it compile to native, apart from the time it takes to maintain that compiler backend.
On top of PS you may pick a FW. Some have React.js under the hood, some follow more closely The Elm Architecture.
Elm is also a language. Surely it's libraries and runtime assume JS and DOM, but nothing prevents creating other ports of the language or creating alternative libraries.
And yes, Elm is opinionated. But that is a consequence of being very simple and pure functional language. As its typesystem does not support using monads or effects to model state with code similar in style to that in non-functional languages, one inevitably ends up with a runtime similar to Elm browser bindings in any Elm embedding.
Essentially Elm is opinionated because its pure functional style is the only style supported by the language with no escape hatches like the do blocks in Haskell or PureScript.
I love Purescript, but I'd be hesitant to recommend it to anyone without a Haskell background. There's a lot more to learn in Purescript, and I'd worry that it could discourage people who are new to the Haskell family of languages.
I'd advise starting with Elm to fall in love with the development experience and wonderful error messages, and graduating to Purescript when you need more power.
I like PureScript a whole lot. Something that ClojureScript and Reason seem to have on PureScript is really efficient persistent structures being the default, at least if I remember correctly. It's been a while, but I seem to recall PureScript giving me nice, functional syntax that under the hood did lots of native JS object / array copies.
Elm provides type safety, immutable data structures, and state management in a very compact but readable way. In fact, Redux was strongly inspired by Elm's state management.
All of these stacks are perfectly acceptable choices, so it comes down to a matter of personal preference. I'd encourage everyone to spend some time with Elm and ReasonML. I find them to be productive and very enjoyable tools.