发布于 2026-01-06 5 阅读
0

ReScript 已经发展了很长时间,也许是时候从 TypeScript 切换到它了?

ReScript 已经发展了很长时间,也许是时候从 TypeScript 切换到它了?

ReScript,这款“来自未来的快速、简洁、完全类型的 JavaScript”,已经存在一段时间了。“ReScript”这个名字是在2020年才正式启用的,但该项目的历史可以追溯到2016年,当时它是由Reason和BuckleScript合并而成。更名的原因在于BuckleScript的目标发生了转变,它试图创建一个融入JavaScript生态系统的语言:JavaScript是唯一的构建目标,支持JavaScript开发者期望的所有特性,例如async/await语法,以及一个易于使用的JavaScript内置函数标准库。

自从更名为 ReScript 以来,项目团队一直非常忙碌。如果你在网上搜索一下人们对 ReScript 的评价,会发现一些用户不太满意的地方,或者缺少一些 JavaScript 功能。这些问题大多已经得到解决,所以让我们来看看最近有哪些改进和功能。希望你能重新审视一下 ReScript,它或许可以成为 TypeScript 的一个强有力的替代方案。

我过去问题的来源

为了整理这份问题列表,我仔细浏览了 ReScript 官方论坛、Hacker News、Reddit 和 Twitter。我力求不遗漏任何内容,而且不仅仅关注已经解决的问题。我会指出一些尚未解决、可能仍然是部分用户痛点的问题,但希望大家能够看到这门语言在过去四年中取得的巨大进步。

在使用 ReScript 之前,我必须先理解 Reason、BuckleScript 和 OCaml。

使用 ReScript 不需要了解 Reason、BuckleScript 或 OCaml 的任何知识。

ReScript 的历史可以追溯到这些语言和工具,但如今它已发展成为一个独立的系统,可以与标准的 JS 生态系统(例如 NPM、PNPM、Yarn 或任何你想要使用的包管理器)无缝集成。它生成的 JS 代码可以被任何构建工具使用,例如 Vite、ESBuild、Webpack 等。ReScript 完全融入了 JS 生态系统,你无需了解任何其他语言、新的构建工具或新的包管理器。

如果你想深入了解 OCaml,它仍然是一门很棒的语言;而Reason作为 OCaml 的另一种语法,也依然非常流行。无论是使用 OCaml 还是 Reason,你都可以编译成本地代码,或者使用 BuckleScript 的延续版本Melange

我应该使用 Belt.Array、Js.Array 还是 Js.Array2?

由于 ReScript 的历史根植于 OCaml 和 Reason,它也继承了这些语言的一些固有特性。这些特性本身并无不妥,但 OCaml 毕竟不是 JavaScript,它有自己独特的模式和实现方式,这些模式和方式都符合 OCaml 的语言特性。当 Reason 的目标是在保持与 OCaml 完全互操作性的同时,编译成原生代码或 JavaScript 时,试图将这些模式与 JavaScript 相匹配就显得十分繁琐。如今,ReScript 专注于 JavaScript 领域,因此它拥有一个全新的核心库,旨在让用户能够轻松上手和理解。之前的标准 JavaScript 库正在被弃用,并将很快从语言中移除,而 Belt 将发展成为一个功能更全面的实用程序库,类似于 Lodash,用户可以选择是否使用。

上述问题的答案就是直接使用Array



let t = [1, 2, 3]->Array.map(n => n + 1)


Enter fullscreen mode Exit fullscreen mode

Core 目前是一个独立的库,但计划在年底前将其直接集成到语言中。

缺少 async、await 和 Promise 会很麻烦。

asyncReScript 10.1 在 2023 年初添加了对/语法的原生支持。await我上面提到的新核心库也引入了一种简化的 Promise 使用方式。



// async/await
let fn = async () => {
  try {
    let result = await getData()
    Console.log(result)
  } catch {
  | err => Console.error(err)
  }
}

// Promises
let fn = () => {
  getData()
  ->Promise.thenResolve(result => Console.log(result))
  ->Promise.catch(err => {
    Console.error(err)
    Promise.resolve()
  })
}


Enter fullscreen mode Exit fullscreen mode

缺乏社区类型

ReScript 早期版本中,用户必须先为大多数现有的 JS 库编写自己的绑定和类型定义才能使用它们。现在,已有数百个 NPM 包提供了绑定和文档,指导用户如何将自己喜欢的 JS 库与 ReScript 结合使用。这些绑定支持React(ReScript 编译器仍然直接提供对 React 的一流支持)、NodeJotaiReact QueryReact Hook FormAntDMaterial UIBunVitestGraphqljs等等!

甚至还有一些非常优秀的 ReScript 优先库,它们真正脱颖而出,能够充分利用 ReScript 强大的类型系统和编译器,例如rescript-relay



/* Avatar.res */
module UserFragment = %relay(`
  fragment Avatar_user on User {
    firstName
    lastName
    avatarUrl
  }
`)

@react.component
let make = (~user) => {
  // this is fully typed!
  let userData = UserFragment.use(user)

  <img
    className="avatar"
    src=userData.avatarUrl
    alt={
      userData.firstName ++ " "
      userData.lastName
    }
  />
}


Enter fullscreen mode Exit fullscreen mode

ReScript 还提供了强大的工具,用于将 JSON 解析为类型。以下是rescript-schema的一个示例:



@schema
type cat = {
name: string,
color: string,
}

let _ =
%raw({ name: 'fluffy', color: 'orange' })
->S.parseWith(catSchema) // catSchema is generated automatically!
->Console.log

Enter fullscreen mode Exit fullscreen mode




我不明白如何使用现有的 JavaScript 库。

虽然目前已有越来越多的社区用 ReScript 编写的绑定和库,但迟早你还是需要深入研究如何编写自己的绑定。好在 ReScript 的文档非常出色,而且你可以参考众多现有的 NPM 包来获取灵感。

就在本周,我需要使用一个函数path-to-regexp,我只花了短短几分钟就创建了绑定和所需的函数。



type t = string => option<{.}>

type m = {
path: string,
params: {.}, // this means any object
}

@unboxed // @unboxed is a way to have a variant type that compiles to simple javascript without a runtime object
type isMatch =
| Yes(option<m>) // the JS for this is just an object of type m
| @as(false) No // the JS for this is just 'false'

type match = string => isMatch

@module("path-to-regexp") // this is the actual binding using the types I made above
external match: string => match = "match"

let make = url => {
let fn = match(url) // and now I can call it!
path =>
switch fn(path) {
| Yes(t) => t->Option.map(t => t.params)
| No => None
}
}

Enter fullscreen mode Exit fullscreen mode




我不能有两个同名文件吗?

这一点一直没变,而且由于模块系统的工作方式,以后也不会变。对于 JavaScript 开发者来说,一开始可能会觉得有点奇怪,但在其他语言中这并不算什么。你会很快习惯的。

我为什么会选择它而不是 TypeScript?

我还有另一篇文章深入探讨了这个问题:厌倦了 TypeScript?试试 ReScript 吧!

简而言之,如果符合以下条件,您应该选择 ReScript:

  • 你需要的是一个强劲、可靠的系统(不要任何类型!)
  • 你需要类型,但又不想编写类型注解(编译器会自动识别!)
  • 你只想使用 JavaScript 的“好部分”
  • 即使你的代码库非常庞大,你也希望在 VSCode 中获得极速的编译器和快速的智能感知功能。
  • 你想要像模式匹配、Option 和 Result 类型以及强大的变体类型这样令人惊叹的下一代语言特性

还不信服?

欢迎留言!我非常乐意更详细地讨论 ReScript,并解答关于它的问题!过去四年里,我一直在几个项目中使用 ReScript,现在需要用到 TypeScript 时,切换回去真的很难。亲眼见证 ReScript 的发展和演变令人惊叹,它绝对应该被视为 TypeScript 的替代方案。

封面照片由Patrick Tomasso拍摄,来自Unsplash

文章来源:https://dev.to/jderochervlk/rescript-has-come-a-long-way-maybe-its-time-to-switch-from-typescript-29he