Difficulty using gentype with polymorphic variants

I’m trying to use a function that takes a string | number | bool and want GenType to validate my bindings are correct. But it sure seems to be a lot of work. The ReScript documentation talks about @unwrap helping with this kind of thing but I couldn’t figure it out. I read somewhere that one great thing about ReScript is that polymorphic variants can get passed to JS without writing any converters. But I can’t get any of it to work. Here is the best I can do. Is there a simpler way? The library I’m binding to has LOTS of weird parameters - string | int, string | string[] - and writing all this kind of code for every such parameter is a pain.

Here is a function I want to bind to in a file variantExperiment.ts

export type Sizing = "inherit" | number | boolean;
export const logSizing = (i: Sizing) => console.log(i);

This is the simplest ReScript I could figure out…

module Sizing = {
  @genType.import(("./variantExperiment", "Sizing"))
  type t

  external toSizing: 'a => t = "%identity"
  let inherit = "inherit"->toSizing
  let fromInt = (i: int) => i->toSizing
  let fromBool = (i: bool) => i->toSizing
}

@genType.import("./variantExperiment")
external logSizing: Sizing.t => unit = "logSizing"

The generated javascript has this, when what I really want is a zero-cost casting.

function fromInt(i) {
  return i;
}

function fromBool(i) {
  return i;
}

I don’t think you need genType to bind to this function. Just bind to it as if it were a JavaScript function, but with type information known beforehand:

module Sizing: {
  type t
  
  @inline("inherit") let inherit: t
  external number: float => t = "%identity"
  external boolean: bool => t = "%identity"
} = {
  type t = string // This is a white lie because we control the module

  @inline let inherit = "inherit"
  external number: float => t = "%identity"
  external boolean: bool => t = "%identity"
}

@module("./variantExperiment")
external logSizing: Sizing.t => unit = "logSizing"

let () = logSizing(Sizing.inherit)

Rule of thumb: let bindings generate code, externals don’t. In this case though ReScript seems to be generating a wrapper for logSizing even though it’s an external. Bug perhaps.

Ok I see how external eliminates any code from the "%identity" scenario. I prefer to use GenType since it validates I’m using the right parameters; it’s almost no extra code compared to what you proposed except for the defining and importing the type. I still don’t understand why unwrap doesn’t work with any of this; isn’t it supposed to?

==
Actually, this comes closer but doesn’t work with gentype. The trick was to use a unit () parameter for one of the polymorphic types to get it to compile but I really want a constant string for that one; @unwrap requires payloads on all of them.

@module("./variantExperiment")
external logSizing: @unwrap [#inherit(unit) | #Num(int) | #Bool(bool)] => unit = "logSizing"