That’s all right! But the DX of editor of ts is very ergonomic, which keeps me using ts all over the time.
I slightly disagree. It’s not about what you can do but how you can do it.
In my opinion, FP-influenced languages make code more aesthetic. Features help reduce code nestings, amount of parenthesis, magic strings, and “point-full” programming. They help to focus on data flow rather than writing code itself.
You could do a lot and probably be even more efficient (performance-wise) with writing WebAssembly by hand (reducing the bloat generated by the backend), but it’s extremely impractical.
The syntax can be your friend with particular programming concepts or your foe.
You can do a lot of programming with FP flavor with JS & TS but languages itself will be your foe because of their syntax.
JS & TS lacks particular things in their syntax:
-
blocks of codes are not expressions
-
lack of real pattern matching (and I’m not only talking about ergonomics of switch-case, but it’s also about destructing and doing additional checks)
-
lack of variant/enums support
this:
type Test1 = {
type: "test1",
value: number
}
type Test2 = {
type: "test2"
}
type Test3 = {
type: "test3"
value: Error
}
type Test4 = {
type: "test4"
value: string
}
type MyTest = Test1 | Test2 | Test3 | Test4;
is not even close to this:
type myTest =
| Test1(int)
| Test2
| Test3(result<string, string>)
| Test4(string)
It might look trivial but it flips how code is developed in practice.
Less code = easier maintenance + less bugs
-
inheritance
Something that I missed so much at the beginning of my journey with Rescritp & Rust. But in practice with large legacy code bases, it’s an enormous pain to track the path of inheritance in extended interfaces (especially in TS with its picks and omits).
In general, code organized in modules is more available than nested in types/interfaces/classes -
null/undefined/error handling
pattern matching a result is far more intuitive than try catching and less error-prone, but it can be done well only with other syntax features like proper variant support, proper pattern matching, proper tuples, code blocks as expressions, and so on… -
(TS only) typescript theoretically supports tuples but the syntax is almost identical with arrays which causes a lot of trouble
The problem with additional libraries for TS is significant, and it’s not about adding and configuring libraries.
It’s about:
- maintenance (updating versions in large code bases)
- consistency (for large teams that often change it’s a challenge to keep rules consistent)
I loved fp-ts & karma but implementing it well with a large team is tough.
Languages like Rescript or Rust did things in an opinionated way and got credit for that.
Languages like Javascript(and Typescript) want to be everything and you can be sure that if something wrong can be done, some developers in your team will do it.
That is the reason why i keep using rescript. The biggest problem i think in rescript is the ide support, especially in native Windows environment.
The biggest pain is that TS has no idea of how to type a tuple unless you provide a type annotation.
const t1 = [1, "a"] // type is Array<string | number>
// you have to do this
const t1: [number, string] = [1, "a"]
For me it’s all about the quality of the type system (and ML languages do very well here):
- Type soundness (there seems little point to a type system without it).
- Hindley-Milner type inference
- ADT’s, / Variants together with pattern matching for better data modelling
That’s the key language features, for tooling etc… it’s
- Compiler speed
- True js alternative, (eg… use of js ecosystem)
Things I’m not so keen on.
- Higher level module system - there are likely better alternatives for the features it provides.
- PPX’s - better alternative’s
Hi, I am in the process of rewriting a F# + Fable + Feliz + Preact app with ReScript.
ReScript’s onboarding was not frictionless. I started with create-rescript-app. Using pnpm, I first encountered a rescript-vscode extension issue that was fixed a few days later. The .mjs files polluting the src directory were not appealing at all. Configuring the ReScript output to generate them in the lib directory created issues with paths. Fortunately I discovered vite-plugin-typescript that solved all of this.
Once these dev tools were finally set, I started playing with the code. I first thought .resi files were mandatory. It was a deal breaker for me. Having function signatures separated from their implementation is a total nonsense, even with the minor benefits of making compilation faster and documentation easier.
I was relieved to learn that .resi files were actually optional, and that you can use %%private to hide some functions of a module. I was surprised to see that setting a screaming case variable like let SNACKBAR_TIMEOUT = 5_000 produced a cryptic “variant constuctor not found” error. Compiler errors could be more explicit.
That is my experience so far. I wrote it down so ReScript people can see what new users like me are experiencing, and hopefully make things smoother.
Cheers,
Laurent
You’re approaching this from a different background, which naturally comes with expectations about how certain things should work.
Using pnpm
There’s a wide range of package manager setups in the JS ecosystem today, and it’s genuinely difficult to keep up with all of them.
The
.mjsfiles polluting thesrcwere not appealing at all.
This is largely a matter of perspective. It’s only a problem if you consider it one. I had the same reaction initially, especially to the fact that files need to be written to disk for Vite to pick them up. Over time you adjust to this and accept that this is not intended to be a 1:1 replacement for the toolchain you’re coming from.
I first thought
.resifiles were mandatory. It was a deal breaker for me.
Signature files are only required to support React Fast Refresh. That nuance isn’t emphasized enough in the documentation. It is mentioned in this blog post, but it’s easy to miss.
I was surprised to see that setting a screaming case variable like
let SNACKBAR_TIMEOUT = 5_000produced a cryptic “variant constuctor not found” error.
This error only feels cryptic if you’re not yet familiar with the language fundamentals. In ReScript, capitalized identifiers are reserved for modules and variant constructors. Documentation can’t realistically enumerate all assumptions brought over from other languages.
Overall, it feels like you’re evaluating ReScript too much through an F# lens. The documentation does cover most of these points, but it does require a linear, end-to-end read rather than selective lookup.
It’s also worth weighing the positives:
-
Much smoother integration with the JS ecosystem. The compiler is distributed via npm and that’s it, no dual package manager setup.
-
Very fast builds. While Fable can be quick, it still depends on slower components like NuGet and MSBuild.
-
Once you’re comfortable with bindings, they’re arguably simpler than their Fable equivalents.
-
A smaller but relevant advantage: if you’re motivated to fix a specific issue, the feedback loop is extremely fast. Maintainers are responsive and changes can land quickly. In contrast, fixes to the F# compiler typically require navigating a much larger process and release pipeline.
For me, the switch was worth it. I do miss some aspects of F# occasionally, but overall I’m very happy with ReScript.
Don’t get me wrong, for now at least, my ReScript first impression is quite positive. Otherwise I would not rewrite my app from F# to ReScript. As you said, maybe the docs could be improved.
5 Reasons:
- It is truly sound, you can’t adjust the degree of typing in your codebase. It is nice not having to dance around null all the time or bikeshed on compiler settings.
- It is simple. It’s very easy to write a switch statement in TypeScript that compiles but is wrong. It is nice to only have one way to write a switch statement in ReScript whose exhaustiveness checking can’t be circumvented deliberately or accidentally.
- It is familiar to both JS and TS devs, making it easy to learn. For people who don’t know ReScript at all, i’m confident it could be maintained without knowing ReScript at all.
- It is ready for the web with React bindings. I’ve integrated ReScript react components into TypeScript codebases, at any level.
- It has a great gradual adoption strategy. It’s presence grows in your codebase. This is a different approach than TypeScript, which is applied (to varying degrees of typing) to all your JS code. This makes the experience different for each project.
I’m convinced these qualities make it the successor of TypeScript.
