3

假设我有以下 ReasonML 类型:

type xEntry = {title: string};
type yEntry = {value: int};
type entry =
  | X(xEntry)
  | Y(yEntry);

我想将以下值导出到 JavaScript 端:

/* Input */
let value = [
  X({title: "foo"}),
  Y({value: 123})
];

如以下结构:

/* Expected output */
[{"type": "X", "title": "foo"},
 {"type": "Y", "value": 123}]

我几乎可以用下面的代码来实现它:

[@bs.deriving abstract]
type jsXEntry = {
  [@bs.as "type"]
  type_: string,
  title: string,
};

[@bs.deriving abstract]
type jsYEntry = {
  [@bs.as "type"]
  type_: string,
  value: int,
};

type jsEntry =
  | JsX(jsXEntry)
  | JsY(jsYEntry);

let fromEntry = entry =>
  switch (entry) {
  | X(v) => JsX(jsXEntry(~type_="X", ~title=v.title))
  | Y(v) => JsY(jsYEntry(~type_="Y", ~value=v.value))
  };

let convertToJs = (entries: list(entry)): Js.Array.t(jsEntry) =>
  Array.map(fromEntry, ArrayLabels.of_list(entries));

不幸的是(但可以理解)我得到以下结果:

/* convertToJs(value) */
[ [ { type: 'X', title: 'foo' }, tag: 0 ],
  [ { type: 'Y', value: 123 }, tag: 1 ] ]

这个结果包含标记的记录(一个单元素数组,带有一个tag属性),这不是我要找的。

我可以在 JavaScript 端编写一个转换函数来摆脱它,但我更愿意直接从 ReasonML 生成正确的结构。

另外,如果可能,我想避免使用%bs.raw构造。

如何产生顶部显示的预期输出?

4

1 回答 1

3

您可以使用抽象类型而不是变体 for jsEntry,然后用于Obj.magic绕过类型系统并将其强制转换为该类型:

type jsEntry;

let fromEntry: entry => jsEntry = 
  fun | X(v) => Obj.magic(jsXEntry(~type_="X", ~title=v.title))
      | Y(v) => Obj.magic(jsYEntry(~type_="Y", ~value=v.value))
;

不过要小心Obj.magic,因为绕过类型系统很容易破坏你的代码。确保始终注释类型以防止类型推断给您带来惊喜。

于 2018-08-11T16:27:58.823 回答