Feb 2, 2023

ReScript 10.1

Async/await & better Promise support, JSX v4, and more!

ReScript Team
Core Development

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 any makeProps convention anymore. make functions will now be transformed into a prop type and a component function. That's it.

  • Two new transformation modes. JSX v4 comes with a classic mode (= React.createElement) and automatic mode (= jsx-runtime calls). The latter is the new default, moving forward with rescript/react@0.11 and React@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!

ReScriptJS Output
// 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 and Js.*. 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.

Want to read more?
Back to Overview