Many modern web developers are embracing emerging technologies that improve the speed of development as well as the quality of their code. For example, JavaScript has a reputation for its flexibility, but is often criticized for its lack of type safety, leading developers to turn to languages like TypeScript and ReScript.
TypeScript and ReScript are type-safe concepts that extend JavaScript functionalities. That means developers can enjoy static typing to complement JavaScript’s dynamism. In this tutorial, we’ll compare ReScript with TypeScript with an overview of the latest release of ReScript, which is v11 as of January 2024.
ReScript is a lightweight typed language that efficiently compiles into human-readable JavaScript. It comes with a very fast compiler toolchain that is capable of scaling to meet any project requirements.
Using OCaml’s strong typing system, ReScript ensures that developers have a powerful system with type interferences, built-in support for JSX, and a very efficient and fast build that can compile to a clean and performant JavaScript. This makes ReScript a good choice for any JavaScript framework.
ReScript is also built to simplify its adoption by developers of all levels.
ReScript v11 comes with a lot of new features and fixes to improve on its existing features. Some of these updates are breaking changes. Let’s look at the new features introduced in ReScript v11:
@deriving(accessors)
outputsrescript
command as the rescript build
command, so now you can do rescript -w
--with-deps
parameter. Instead, it’s done automatically when you run the rescript build
command@as
property to rename fields in inline entries@as
attribute allows you to rename object attributes in @obj
external PPXTypeScript has been around for a while and has set the rules regarding how we use variables and functions in JavaScript, making it easier to spot mistakes before they cause problems. It has also inspired others to adopt similar ideas, making coding cleaner and less error-prone for everyone.
ReScript was developed to address the same problem, but uses a different approach. We’ll see what ReScript adds as well as what it leaves behind by comparing both tools through syntax and language features, type system interoperability with JavaScript, ecosystem, tooling, and learning curve.
ReScript’s syntax is influenced by OCaml, which uses a functional programming style that aims at simplifying things for developers who understand functional paradigms. It has a clear syntax, especially for defining types and pattern matching, making it more readable for those accustomed to functional programming languages.
Here’s an example of defining a type and function in ReScript:
// Defining a type and function in ReScript type person = { name: string, age: int } let greet = (p: person) => "Hello, " ++ p.name
Meanwhile, TypeScript extends JavaScript with static types, which integrate seamlessly with existing JavaScript syntax. It supports both object-oriented and functional programming paradigms.
Being a superset of JavaScript, any valid JavaScript code is also valid TypeScript — after adding type annotations — which makes it very accessible for JavaScript developers.
Here’s an example of defining an interface and function in TypeScript:
// Defining an interface and function in TypeScript interface Person { name: string; age: number; } const greet = (p: Person): string => `Hello, ${p.name}`;
ResScript uses auto-detection on most data types, minimizing manual coding, efficiently dealing with complex data, and controlling the program flow:
// Pattern matching in ReScript
type shape =
| Circle(float) // radius
| Rectangle(float, float) // width, height
let area = (s: shape) =>
switch s {
| Circle(r) => 3.14 *. r *. r
| Rectangle(w, h) => w *. h
}
Unlike ReScript, which has a strict and powerful type system in which the developer must explicitly type their comments, TypeScript is designed for JavaScript. It performs type checking, but in less strict way:
// Using union types and type guards in TypeScript type Shape = | { kind: "circle"; radius: number } | { kind: "rectangle"; width: number; height: number }; function area(s: Shape): number { if (s.kind === "circle") { return Math.PI * s.radius ** 2; } else { return s.width * s.height; } } =
ReScript’s efficient code compilation makes it a machine-readable language. On the other hand, combining it with languages such as Java can be tricky and requires some customization, thus making its adoption a little bit slow:
// Binding to a JavaScript function in ReScript @val external alert: string => unit = "alert" alert("Hello from ReScript!")
TypeScript, which works well with JavaScript, is later on compiled to be an enhanced version of JavaScript that communicates with other languages effortlessly. It’s also compatible enough for different languages to be effortlessly molded into TypeScript, which makes the learning process much faster and easier:
// Directly calling a JavaScript function in TypeScript alert("Hello from TypeScript!");
Being an open source language that originated in 2012, TypeScript has a huge community of developers. Consequently, it has a large number of useful resources, which include tutorial videos and forums where anyone can come and learn something easily.
Moreover, TypeScript provides ease of operation as it includes the use of tools such as linters, IDEs, and build tools. You’re even free to choose and use the multiple libraries readily available for most functionalities that you may need to look into, along with easy-to-access resources.
There is a growing community for ReScript, but it’s not as big as that of TypeScript. This is because ReScript is yet to be widely adopted by organizations and the community.
To further see how ReScript does things differently from TypeScript, let’s explore a real-world application of ReScript in a React application. To get started, create a new ReScript application by running the command below:
npx create-rescript-app
The above command will prompt you to select the project name my-rescript-app
. For the template, we’ll use Next.js and ReScript‘s latest version as well as its latest ReScript Core version:
Now you can build and run the Next.js application by running the command below:
npm run res:build && npm run dev
Now that the application is running is running, let’s update the project to build a to-do application.
We’ll start by creating the types for the todo application. In the src/index.rs
file, update it to add the code below:
type todo = { title: string, status: bool, } type state = { todos: array<todo>, inputValue: string, }
The above code defines the the type for what each todo object would look like. It will have a title
and status
fields. Then we defined another type for the state of the application, for handling the todos state, and the form input value for creating new todos.
Next, create actions and reducers to handle the creating, deleting and input change. Add the code snippet below to the index.rs
file:
... type actions = | CreateTodo(todo) | DeleteTodo | InputChange(string) let reducer = (state, action) => switch action { | CreateTodo(todo) => { todos: state.todos->Js.Array.concat([todo]), inputValue: state.inputValue, } | DeleteTodo => {todos: [], inputValue: state.inputValue} | InputChange(newValue) => {todos: state.todos, inputValue: newValue} }
The above code snippet will define a reducer function that will allow us to manage the state of our to-do application. We defined three actions that can be dispatched to the reducer:
CreateTodo
for adding a new to-do item to the existing list of todosDeleteTodo
to clear all the todosInputChange
methods to handle the input field state change when the user starts typingThe reducer
function takes the current state and an action and then returns a new state based on the action type to allow for predictable state transitions.
Now let’s use the actions and reducers we created to render the to-dos. We’ll also add event listeners to handle creating new to-do items:
let default = () => { let (state, dispatch) = React.useReducer(reducer, initialState) let handleInput = e => { let newValue = ReactEvent.Form.target(e)["value"] newValue->InputChange->dispatch } <div> <h1 className="text-3xl font-semibold"> {"My Todo List"->React.string} </h1> <div className="my-4"> <input type_="text" className="w-full rounded-full p-2 border border-gray-500 placeholder-gray-400" value=state.inputValue onChange={handleInput} /> <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mt-4" onClick={_ => { let newTodo = {title: state.inputValue, status: false} newTodo->CreateTodo->dispatch }}> {"Create Todo"->React.string} </button> </div> {state.todos ->Array.map(todo => <div key=todo.title className="mb-4"> <h2 className="text-xl font-bold"> {todo.title->React.string} </h2> </div> ) ->React.array} </div> }
In the above code snippet, we used the useReducer
Hook to initialize the state of our component, which will accept a reducer function and the value of the initialState
then returns the current state and dispatches to update the state of the component.
Lastly, run the command below to build and re-start the application:
npm run build && npm run dev
The resulting application should look something like the below:
When choosing between ReScript and TypeScript for application development, the project scope and planned application features are two issues that come to mind.
ReScript is among the most popular tools because it supports functional programming. It also lets programmers leverage the big React ecosystem, including TypeScript-based tools for managing processes in this ecosystem and the set of functional programming approach that are safely typed.
TypeScript is the perfect language for projects that use the larger JavaScript ecosystem in a significant way, including third-party libraries and tools. It brings both JavaScript and TypeScript together to reduce or even eliminate the transition requirement and allow you to use the same language in the frontend and backend for deployment in a variety of cases.
To help you in choosing between ReScript and Typescript, I’ve summarized the features that both tools share and their ideal use cases:
Features | ReScript | TypeScript |
---|---|---|
Syntax and language features | ReScript is largely based on OCaml, but it follows a functional programming style and has a clear syntax for defining types and patterns | Extends JavaScript with static types of objects and supports both object-oriented and functional programming techniques. Any JavaScript code that runs can also run in TypeScript |
Type system | Strong type system with auto-detection, pattern matching, and explicit type annotations | Looser-type systems and types designed for JavaScript also support union types |
Interoperability with JavaScript | More efficient for code compilation, but combining it with a different language like Javascript can be challenging | Compatible with JavaScript since it converts the code into enhanced JavaScript that can easily communicate with other languages in the JavaScript environment |
Ecosystem and tooling | Smaller community than Typescript’s, but becoming more popular | Large community with a great number of tutorials, forums, linters, IDEs, and libraries to help with development |
Ideal use case | ReScript is ideal for large-scale applications because of its immutable data structure and declarative programming paradigm. Also, its strong type system makes it easier for developers to catch error during code compilation rather than runtime, which helps to maintain large-scale applications | TypeScript is ideal for full-stack applications since it can be used on the client and server side of the application helping developers avoid repetition, maintaining type-safety throughout the application, and ensuring consistency across the application layers. Also, its gradual adoption model makes it easier for developers to use it in an existing Javascript project |
Ultimately, it’s necessary for the option you choose to be consistent with the aims of your project, strengths of your team, and the type of environment spatial development that will take place.
In this tutorial, we’ve covered the difference between ReScript and Typescript to understand what each of them brings to table and what they both do differently.
We discussed what ReScript is and looked at the new features in ReScript v11. Then we went further to compare the features of ReScript with those of TypeScript. Finally, we built a to-do application to demonstrate how to use ReScript in a practical scenario.
Now that you’ve learned the features ReScript brings to web development, would you use it in your next project?
LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowHandle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
Design React Native UIs that look great on any device by using adaptive layouts, responsive scaling, and platform-specific tools.
Angular’s two-way data binding has evolved with signals, offering improved performance, simpler syntax, and better type inference.