Constructing Javascript Arrays of different types

Hello Everyone,

I have this javascript code (taken from URL) which I am converting to rescript.

const map = new Map({
  layers: [rasterLayer, vectorLayer],
  target: document.getElementById('map'),
  view: new View({
    center: fromLonLat([2.896372, 44.6024]),
    zoom: 3,
  }),
});

My code is working so far when I pass only the tilelayer object

type mapParams = {
  target: string,
  layers: array<tilelayer>,
  view: view
}
type map
@module("ol/Map") @new external createMap : mapParams => map = "default"
    let _ = createMap({
      target: "map",
      layers: [tileLayer],
      view: createView({projection: "EPSG:4326", center: [-121.02524898891282, 47.440536890633204], zoom: 8.7})
    })

Very good so far. Now If I construct an object of type VectorLayer I cannot pass it in the layers array because the compiler says that the type must be tile layer.

so I changed my code to

type layer = 
| TileLayer(tilelayer)
| VectorLayer(vectorlayer)

type mapParams = {
  target: string,
  layers: array<layer>,
  view: view
}
    let _ = createMap({
      target: "map",
      layers: [TileLayer(tileLayer), VectorLayer(vectorLayer)],
      view: createView({projection: "EPSG:4326", center: [-121.02524898891282, 47.440536890633204], zoom: 8.7})
    })

But the code above crashes at runtime because the passed objects are not what javascript wants. rescript compiler is generating the code for TileLayer and VectorLayer as

 var tileLayer = {
            TAG: /* TileLayer */0,
            _0: new Tile({
                  title: "OSM",
                  type: "base",
                  visible: true,
                  source: osm
                })
          };
          var vectorLayer = {
            TAG: /* VectorLayer */1,
            _0: new Layer.Vector({
                  source: vectorSource
                })
          };

but javascript library (openlayers) wants the array to be like [new Tile(...), new Vector(...)]

Here is my working code in Rescript playground working

And here is the not working code which crashes at runtime (compiles fine)

1 Like

You have hit on one of the troubles of binding to JS…when the internal representation doesn’t match your JS expectation.

One way to solve it is with an abstract type and externals that are just the %identity. Here is an example:

module Layer = {
  type t
  external tileLayer: tilelayer => t = "%identity"
  external vectorLayer: vectorlayer => t = "%identity"
}

type mapParams = {
  target: string,
  layers: array<Layer.t>,
  view: view
}

// ... snip ...

    let tileLayer = Layer.tileLayer(createTileLayer({title: "OSM", type_: "base", visible: true, source: osm}))
    let vectorLayer = Layer.vectorLayer(createVectorLayer({source: vectorSource}))

// ... snip ...

And generated code:

          var tileLayer = new Tile({
                title: "OSM",
                type: "base",
                visible: true,
                source: osm
              });
          var vectorLayer = new Layer.Vector({
                source: vectorSource
              });

Here is your playground updated with that example.

Further reading:

1 Like