TypeScript 和 ReasonML 都声称自己为 Web 开发人员提供了可编译为 JavaScript 的静态类型语言,那么它们之间的差别是什么?ReasonML 能带来 TypeScript 想要做到的一切(甚至更多),但是前者没有那些 JavaScript 怪癖。这样的话,你是否应该试试它呢?
TypeScript 是 JavaScript 的超集,这既是它的最佳特性,也是它最大的缺陷。虽说与 JavaScript 的相似性给了人熟悉的感觉,但这意味着我们所喜爱和反感的所有 JavaScript 怪癖都在 TypeScript 里重现了。TS 只不过是在 JavaScript 之上添加了类型,然后就差不多完事了。
ReasonML 提供的是一种完全不同但让人感觉很熟悉的语言。这是否意味着 JavaScript /TypeScript 开发人员会很难学习这个新语言?我们就来看看吧。
声明一个变量
让我们从变量声明开始:
在 ReasonML 中,我们使用 let 关键字声明一个变量。没有 const,默认情况下 let 是不可变的。
在这种情况下,两种语言都可以推断出 a 的类型。
函数
TypeScript
ReasonML
尽管我没有手动编写任何类型,但这个函数的参数还是类型化了。为什么我们用不着在 ReasonML 中编写类型呢?因为强大的类型系统可以进行出色的类型推断。这意味着编译器可以在不需要你帮助的情况下推断出类型。ReasonML 中的 (+) 运算符仅适用于整数——a 和 b 只能是这种类型,因此我们不必编写它们。但如果需要,你随时都可以编写类型:
ReasonML
接口,记录
ReasonML 中最接近接口(Interface)的是记录(Record,https://reasonml.github.io/docs/en/record)。
ReasonML
记录就像 TypeScript 对象一样,但前者是不可变的,固定的,并且类型更严格。下面我们在某些函数中使用定义的结构:
ReasonML
TypeScript
同样,我们不需要注释类型!在这个函数中我们有一个参数 product,其属性 name 为字符串类型。ReasonML 编译器可以根据使用情况猜测该变量的类型。因为只有 product 这个类型具有字符串类型的 name 属性,编译器会自动推断出它的类型。
更新记录
ReasonML 支持展开运算符,并像 TypeScript 一样对名称和值做类型双关。
看看 ReasonML 生成了什么样的 JavaScript 吧,这也很有趣:
ReasonML 中的记录表示为数组。如果人类可以像编译器一样记住每种类型的每个属性的索引,则生成的代码看起来就会像人类编写的那样。
Reducer 示例
我认为这就是 ReasonML 真正闪耀的地方。我们来比较一下相同的 Reducer 实现:
在 TypeScript 中(遵循这份指南:https://redux.js.org/recipes/usage-with-typescript)
没什么特别的,我们声明了状态界面、默认状态、动作、动作创建者以及最后的 Reducer。
在 ReasonML 中也是一样:
对,就这些。
让我们看看这里发生了什么。
首先,有一个状态类型声明。
之后是动作 Variant(https://blog.dubenko.dev/typescript-vs-reason/#https://reasonml.github.io/docs/en/variant)类型:
这意味着具有类型 action 的任何变量都可以具有以下值之一:Reset、带有一些字符串值的 AddMovie 和带有一些字符串值的 RemoveMovie。
ReasonML 中的 Variant 是一项非常强大的功能,可让我们以非常简洁的方式定义可以包含值 A 或 B 的类型。是的,TypeScript 有联合类型,但它没有深入集成到语言中,因为 TypeScript 的类型是给 JavaScript 打的补丁;而 Variant 是 ReasonML 语言的重要组成部分,并且与模式匹配等其他语言功能紧密相连。
说到模式匹配,我们来看一下 Reducer。
我们在这里看到的是一个函数,该函数接受状态作为第一个参数,然后将第二个参数与可能的值匹配。
我们也可以这样写这个函数:
由于匹配参数是 ReasonML 中的常见模式,因此这类函数以较短的格式编写,如前面的代码片段中所示。{ movies: [movie, …state.movies] }这部分看起来与 TypeScript 中的一样,但是这里发生的事情并不相同!在 ReasonML 中,[1,2,3] 不是数组,而是不可变的列表。可以想象它是在语言本身内置的 Immutable.js。在这一部分中我们利用了一个事实,即 Append 操作在 ReasonML 列表中的时间是恒定的!如果你之前用的是 JavaScript 或 TypeScript,你可能会随手写下这种代码,无需太多顾虑,并且可以免费获得性能提升。
现在,让我们看看向 Reducer 添加新动作的操作。在 TypeScript 中是怎么做的呢?首先你要在类型定义中添加一个新动作,然后以动作创建者的形式编写一些样板,当然不要忘了在 Reducer 中实际处理这种情况,一般这一步都容易被忽略。
在 ReasonML 中,第一步是完全相同的,但是后面就都不一样了。在将新动作添加到类型定义后单击保存时,编译器会带你在代码库中慢慢挪动,处理对应的情况。
你会看到这样的警告:
这是很好的开发体验。它会指出你要处理新动作的确切位置,并且还会准确告诉你缺少哪种情况。
Null、undefined vs Option
在 TypeScript 中我们需要承受 JavaScript 留下来的负担,也就是用 null 和 undefined 来表示几乎一模一样的事物——毫无意义。
在 ReasonML 中没有这种东西,只有 Option 类型。
ReasonML
这是一个大家很熟悉的 Variant 类型。但是它也有一个类型参数’a。这很像其他语言中的泛型,比如说 Option。
来对比更多的代码看看:
访问可以为空的属性是最简单的情况之一,闭着眼也能写出来。在 TypeScript 中,我们可以启用严格的 null 检查然后手动检查 undefined 的值来纠正它。
在 ReasonML 中,我们可以使用内置的 option 类型和 Belt 标准库中的辅助函数,然后就能以标准化方式处理可能为空的值。
带标签的参数
我觉得所有人都会认为带标签的参数功能真是太棒了。可能每个人都必须在某个时候查询函数参数的顺序或含义。不幸的是,TypeScript 中没有带标签的参数。
TypeScript
在 ReasonML 中,你在参数名称前加一个~ 字符,然后它就被标记了。
ReasonML
是的,你可以在 TypeScript 中尝试使用对象作为参数来模拟这种做法,但是随后你需要在每个函数调用时分配一个对象 :/
TypeScript
模块系统
在 TypeScript 中,我们显式导出和导入文件之间的所有内容。
Hello.ts
在 ReasonML 中,每个文件都是一个带有该文件名的模块。
Hello.re
你可以 open 模块,使内容可用而无需模块名称前缀:
编译速度
为了对比编译速度,我们来编译 TodoMVC,因为其实现和项目大小都是容易对比的。我们正在测试的是将代码转换为 JavaScript 所花费的时间。没有打包,压缩等操作。[注 1]
TypeScript + React.js
6.18 秒
ReasonML + ReasonReact
0.96 秒
现在这还包括编译 ReasonML 依赖项的时间,我们还可以测试只编译项目文件的时间:
ReasonML + ReasonReact, only src/
0.33 秒
竟然有这么快!
BuckleScript 将安装时、构建时和运行时的性能视为一项重要功能
这是从 BuckleScript 文档中引用的。BuckleScript 是将 ReasonML 转换为 JavaScript 的工具。
目前 TypeScript 胜过 ReasonML 的地方
关于 ReasonML 的内容并不是都那么美好。它是一种相当新的语言……嗯,实际上它不是基于 OCaml 的,后者已经相当老了;但重点在于互联网上的资源仍然不是特别多。与 ReasonML 相比,用谷歌搜索 TypeScript 的问题更容易获得答案。
DefinitelyTyped 类型实在太多了,ReasonML 想要追上来还有很长的路要走。
结语
对于前端开发人员来说,ReasonML 语法应该让人觉得很熟悉,这意味着学习曲线的起点并不那么陡峭(但仍然比 TypeScript 要陡)。ReasonML 充分利用了其他语言和工具(例如 Immutable.js 和 eslint)中最好的东西,并将其带入了语言级别。它并没有试着成为一种完全纯粹的编程语言,你需要的话可以随时退回到突变和命令式编程。它非常快,它的速度是提升开发体验的重点所在。ReasonML 能带来 TypeScript 想要做到的一切(甚至更多),但是前者没有那些 JavaScript 怪癖。你应该试试它!
注释
[1] 编译时间测试平台是一部 MacBook Pro 2015,处理器是 Intel Core i5-5287U @2.90GHZ
TypeScript + React.js 源码(https://github.com/tastejs/todomvc/tree/master/examples/typescript-react)
ReasonML + ReasonReact 源码(https://github.com/reasonml-community/reason-react-example/tree/master/src/todomvc)
原文链接:
https://blog.dubenko.dev/typescript-vs-reason/
评论