对象

ReScript 对象和记录很像,但:

  • 不需要类型声明。

  • 结构化并且更加多态,和记录不同

  • 不支持更新,除非对象来自 JS 端。

  • 不支持模式匹配

尽管 ReScript 记录可以编译成干净的 JavaScript 对象,但 ReScript 对象是模拟/绑定到 JS 对象的更好的选择,如你所见。

类型声明

声明是可选的,与记录不同。对象的类型是从值中推断出来的,所以你从来不需要写下它的类型定义。尽管如此,这里是它的类型声明语法:

ReScriptJS Output
type person = {
  "age": int,
  "name": string
};

看起来很像记录类型的语法,但是字段名有引号。

创建对象

可以这样创建一个新的对象:

ReScriptJS Output
let me = {
  "age": 5,
  "name": "Big ReScript"
}

注意:如上所述,与记录不同的是,这个 me不会试图找到一个符合 "age""name" 字段的类型声明;相反,"me" 的类型被推断为 "{"age": int, "name": string}"。这很方便,但也意味着下面的代码通过了类型检查但不会报错:

ReScriptJS Output
type person = {
  "age": int
};

let me = {
  "age": "hello!" // age is a string. No error.
}

因为类型检查器并不试图将 meperson 类型进行匹配,如果你想强制一个对象的值是一个预先声明的对象类型,只需标注这个值:

RES
let me: person = { "age": "hello!" }

现在类型系统将正常报错。

访问字段

ReScriptJS Output
let age = me["age"]

更新对象

更新是不被允许的,除非该对象是一个来自于 JavaScript 端的绑定。在这种情况下,使用 =

ReScriptJS Output
type student = {
  @set "age": int,
  @set "name": string,
}
@module("MyJSFile") external student1: student = "student1"

student1["name"] = "Mary"

合并类型

你可以使用 ... 将一个对象类型的定义展开到另一个对象中:

ReScriptJS Output
type point2d = {
  "x": float,
  "y": float,
}
type point3d = {
  ...point2d,
  "z": float,
}

let myPoint: point3d = {
  "x": 1.0,
  "y": 2.0,
  "z": 3.0,
}

这只适用于对象类型,而不是对象的值!

技巧和诀窍

由于对象不需要类型声明,而且 ReScript 会为你推断出所有的类型,所以你可以非常快速和容易地(同时也是危险地)绑定到任何 JavaScript API。检查 JS 输出标签:

ReScriptJS Output
// The type of document is just some random type 'a
// that we won't bother to specify
@val external document: 'a = "document"

// call a method
document["addEventListener"]("mouseup", _event => {
  Js.log("clicked!")
})

// get a property
let loc = document["location"]

// set a property
document["location"]["href"] = "rescript-lang.org"

external 特性和上面技巧的用法在后面的 external 章节给出。这是一个优秀的方法,可以开始编写一些 ReScript 代码,而不必担心是否存在与某个特定库的绑定关系。