拆箱

考虑一个单 payload 变体和一个单字段记录:

ReScriptJS Output
type name = Name(string)
let studentName = Name("Joe")

type greeting = {message: string}
let hi = {message: "hello!"}

如果你检查 JavaScript 的输出,你将看到 studentNamehi JS 对象,正如预期的那样(详见变体的 JS 输出记录的 JS 输出)。

对于需求性能和某些与 JavaScript 互操作情况,ReScript 提供了一种从输出中解包(也就是拆箱)JS 对象包装的方法,它用于单字段记录和单 payload 单构造器变体。用属性 @unboxed 来标注它们的类型声明:

ReScriptJS Output
@unboxed
type name = Name(string)
let studentName = Name("Joe")

@unboxed
type greeting = {message: string}
let hi = {message: "hello!"}

检查新的输出!嗯,很干净。

用法

为什么你会想要只有单个 payload 的变体或记录?为什么不直接...直接传递 payload ?这是变体的一种使用场景。

假设你有一个具有局部/全局坐标系的游戏:

ReScriptJS Output
type coordinates = {x: float, y: float}

let renderDot = (coordinates) => {
  Js.log3("Pretend to draw at:", coordinates.x, coordinates.y)
}

let toWorldCoordinates = (localCoordinates) => {
  {
    x: localCoordinates.x +. 10.,
    y: localCoordinates.x +. 20.,
  }
}

let playerLocalCoordinates = {x: 20.5, y: 30.5}

renderDot(playerLocalCoordinates)

哦,出错了!renderDot 应该使用全局坐标系,而不是局部的... 让我们防止传递错误的坐标系:

ReScriptJS Output
type coordinates = {x: float, y: float}
@unboxed type localCoordinates = Local(coordinates)
@unboxed type worldCoordinates = World(coordinates)

let renderDot = (World(coordinates)) => {
  Js.log3("Pretend to draw at:", coordinates.x, coordinates.y)
}

let toWorldCoordinates = (Local(coordinates)) => {
  World({
    x: coordinates.x +. 10.,
    y: coordinates.x +. 20.,
  })
}

let playerLocalCoordinates = Local({x: 20.5, y: 30.5})

// This now errors!
// renderDot(playerLocalCoordinates)
// We're forced to do this instead:
renderDot(playerLocalCoordinates->toWorldCoordinates)

现在 renderDot 只接受 worldCoordinates。通过使用不同的变体类型 + 参数解构这个完美结合,我们在不影响性能的情况下获得了更好的安全性:unboxed 属性编译为干净的、无变体包装的 JS 代码!检查一下输出。

至于单字段的记录,它们的使用情况有点太前卫了。我们不会在这里提到它们。