如何在类型参数中使用展开的多态变体 [union type]?

How do I use an unwrapped polymorphic variant [union type] in a type parameter?

目标:绑定到 Service Worker Cache

我正在编写一个绑定,让我可以在 ReScript 中编写 Service Worker。字符串 URL 和 Request 有时可以互换使用。

我会尽可能避免 JS 输出中的噪音。

我所知道的[@bs.unwrap]

我知道我可以像这样使用 [@bs.unwrap]add 方法编写绑定

[@bs.send]
  external add: (cache, [@bs.unwrap] [ `Request(Request.t) | `String(string)])
  => Js.Promise.t(unit) = "add";

这是一个简单的用法。

问题:绑定 array 个请求和/或字符串

然而,addAll 方法具有更复杂的类型签名。它采用对象数组,可以是数组或请求或字符串数​​组或具有两种类型项目的数组。

但据我所知,你不能像

这样在类型参数中拆箱类型
[@bs.send]
  external addAll: (cache,
   array([@bs.unwrap] [ `Request(Request.t) | `String(string)])
  => Js.Promise.t(unit) = "addAll";

问题:这种绑定可以在 ReScript 中建模吗?

当然,放弃字符串大小写并使用 Requests 或编写两个单独的绑定并假设我不需要同时具有两者的数组是合理的。

但现在我很好奇:有没有办法在 ReScript 的绑定中模拟这种类型的输入?

您可以使用抽象类型和一组转换函数将值“转换”为该类型,而不是多态变体:

module Value = {
  type t;
  external request: Request.t => t = "%identity";
  external str: string => t = "%identity";
};

[@bs.send]
  external addAll: (cache, array(Value.t)) => Js.Promise.t(unit) = "addAll";

用法示例:

addAll(cache, [|
  Value.request(req),
  Value.str("foo"),
|])

或为简洁起见使用本地打开:

addAll(cache, Value.[|
  request(req),
  str("foo"),
|])

这也是 Js.Json 编码器的工作方式,如果您曾经想过的话。由于 Js.Json 也有解码器,因此您知道如果需要,也可以采用其他方式。不过,这样做有点复杂,并且取决于已被抽象掉的底层类型。

顺便说一句,这是 from my BuckleScript cookbook,它还有很多其他的食谱,在这种棘手的情况下可能会派上用场。