ReScript 10.1
Async/await & better Promise support, JSX v4, and more!
Introduction
We are happy to announce ReScript 10.1!
ReScript is a robustly typed language that compiles to efficient and human-readable JavaScript. It comes with one of the fastest build toolchains and offers first class support for interoperating with ReactJS and other existing JavaScript code.
Use npm
to install the newest 10.1 release:
npm install rescript # or npm install rescript@10.1
This version comes with two major language improvements we've all been waiting for. async/await support for an easy way to write asynchronous code in a synchronous manner, and a new JSX transform with better ergonomics, code generation and React 18 support.
Alongside the major changes, there have been many bugfixes and other improvements that won't be covered in this post.
Feel free to check the Changelog for all the details.
New async
/ await
syntax
Async / await has arrived. Similar to its JS counterparts, you are now able to define async
functions and use the await
operator to unwrap a promise value. This allows writing asynchronous code in a synchronous fashion.
Example:
RES// Some fictive functionality that offers asynchronous network actions
@val external fetchUserMail: string => promise<string> = "GlobalAPI.fetchUserMail"
@val external sendAnalytics: string => promise<unit> = "GlobalAPI.sendAnalytics"
// We use the `async` keyword to allow the use of `await` in the function body
let logUserDetails = async (userId: string) => {
// We use `await` to fetch the user email from our fictive user endpoint
let email = await fetchUserMail(userId)
await sendAnalytics(`User details have been logged for ${userId}`)
Js.log(`Email address for user ${userId}: ${email}`)
}
To learn more about our async / await feature, check out the relevant manual section.
New promise
builtin type and Js.Promise2
module
In previous versions of ReScript, promises were expressed as a Js.Promise.t<'a>
type, which was a little tedious to type. From now on, users may use the promise<'a>
type instead.
Quick example of a .resi
file using the new promise
type:
RESI// User.resi
type user
let fetchUser: string => promise<user>
Way easier on the eyes, don't you think? Note that the new promise
type is fully compatible with Js.Promise.t
(no breaking changes).
Additionally, we also introduced the Js.Promise2
module as a stepping stone to migrate Js.Promise
based code to a first-pipe (->) friendly solution. For the daily practise you'll almost always want to use async
/ await
to handle promises.
(Sidenote: We are also well aware that our users want a solution to unify Belt
, Js
and Js.xxx2
and have a fully featured "standard library" instead of adding more Js.xxx2
modules. Good news is that we have a solution in the pipeline to fix this. Js.Promise2
was introduced to ease the process later on and is not supposed to be the panacea of promise handling.)
If you are already using a third-party promise library like ryyppy/rescript-promise or similar, there's no need to migrate any existing code. Introduce async
/ await
gradually in your codebase as you go.
New JSX v4 syntax
ReScript 10.1 now ships with JSX v4. Here's what's new:
Cleaner interop. Due to recent improvements in the type checker, the
@react.component
transformation doesn't require anymakeProps
convention anymore.make
functions will now be transformed into aprop
type and a component function. That's it.Two new transformation modes. JSX v4 comes with a
classic
mode (=React.createElement
) andautomatic
mode (=jsx-runtime
calls). The latter is the new default, moving forward withrescript/react@0.11
andReact@18
.Allow mixing JSX configurations on the project and module level. Gradually mix and match JSX transformations and modes without migrating any old code!
Pass
prop
types to@react.component
. You can now fine tune@react.component
with your specific prop type needs. Very useful for libraries and frameworks to define component interfaces.Less boilerplate when using
React.Context
. Check out our example for comparison.Revisited props spread operator. This will allow users to spread records in JSX without sacrificing their sanity. Note that this implementation has harder constraints than its JS counterpart. (requires
rescript/react@0.11
or higher)Better type inference of props. Type inference when passing e.g. variants that are defined in the same module as the component is much improved. With the earlier JSX version, you'd often need to write code like this in order for the compiler to understand which variant you're passing:
<Button variant=Button.Primary text="Click" />
. With JSX v4, you won't need to tell the compiler where the variant you're passing is located:<Button variant=Primary text="Click" />
.
Code tells more than words, so here's a non-exhaustive code example to highlight the different JSX features. Make sure to also check out the JS output and play around with the code in our newest playground!
// Set the jsx configuration per module
@@jsxConfig({version: 4, mode: "automatic"})
module AutomaticModeExample = {
// "automatic" mode will compile jsx to the React 18 compatible
// jsx-runtime calls
@@jsxConfig({version: 4, mode: "automatic"})
@react.component
let make = (~name) => {
<div> {React.string(`Hello ${name}`)} </div>
}
}
module ClassicModeExample = {
// "classic" mode will compile jsx to React.createElement calls
@@jsxConfig({version: 4, mode: "classic"})
@react.component
let make = (~name) => {
<div> {React.string(`Hello ${name}`)} </div>
}
}
module NoAttributeExample = {
// No need for `makeProps` anymore
type props = {name: string}
let make = (props: props) => {
<div> {React.string(`Hello ${props.name}`)} </div>
}
}
module ReactInterfaceExample: {
@react.component
let make: (~name: string, ~age: int=?) => React.element
} = {
@react.component
let make = (~name, ~age=0) => {
<div>
{React.string(
`Hello ${name}, you are ${Belt.Int.toString(age)} years old.`,
)}
</div>
}
}
module PropTypeInjectionExample = {
// Let's assume we have a prop type that we wanna enforce
// as our labeled arguments
type someoneElsesProps = {isHuman: bool}
// Here we tell the `react.component` decorator what props to infer.
// Useful for e.g. NextJS usage, or to create components that should
// comply to certain library component interfaces
@react.component(: someoneElsesProps)
let make = (~isHuman) => {
let msg = switch isHuman {
| true => "hello human"
| false => "hello fellow computer"
}
<div> {React.string(msg)} </div>
}
}
module PropSpreadExample = {
// Note: This will require @rescript/react 0.11 or later
@@jsxConfig({version: 4, mode: "automatic"})
@react.component
let make = () => {
let props = {NoAttributeExample.name: "World"}
<NoAttributeExample {...props} />
}
}
let root =
<div>
<AutomaticModeExample name="Automatic" />
<ClassicModeExample name="Classic" />
<NoAttributeExample name="NoAttribute" />
<ReactInterfaceExample name="Interface" />
<PropTypeInjectionExample isHuman=true />
<PropSpreadExample />
</div>
How to migrate to JSX v4?
We provide a full migration guide with all the details of an migration.
Make sure to also check out the rescript-react changelog as well.
What's next?
Our contributors are already one step ahead and are currently working on improvements for the next major v11 release. Things that are currently being explored:
Make uncurried functions the default. This will be a huge change in terms of how we do interop and will open completely new ways to interact with existing codebases. It will also allow us to improve tooling in ways that wouldn't have been possible in a curried language.
Explorations for a community "standard library" that goes beyond
Belt
andJs.*
. This will also involve disabling / removing global "Stdlib" modules that shouldn't be used (e.g.Array
,List
, etc).New tooling to generate markdown from docstrings (module, type and value level). This will be super simple, but very effective.
Explorations for a localized documentation page (currently in a slowed-down exploration phase, but we will be getting there)
Check out the v11 milestone on our rescript-lang
repo for more details on future improvements.
Acknowledgements
As always, we want to thank our contributors for building an amazing platform. Special thanks go out to mununki for building the new JSX v4 syntax. Amazing work!
That's it
We hope you enjoy the newest improvements as much as we do.
In case there's any issues / problems, make sure to report bugs to rescript-lang/rescript-compiler (language / syntax / jsx), rescript-lang/rescript-react (React 16 / 18 binding) or rescript-association/rescript-lang.org (documentation) repositories.
Also feel free to visit the ReScript forum to ask questions and connect with other ReScripters.