How to work with 'this' from JavaScript

Hi all,

First of all, thanks for providing this forum! I’m an avid JavaScript developer, but just getting into ReScript.
The documentation and forum so far have been super helpful.

I’m gradually migrating a CucumberJS project to ReScript. But running into issues because of how CucumberJS passes ‘this’.

The relevant snippet of JavaScript I’m trying to convert:

var Cucumber = require("@cucumber/cucumber");

Cucumber.Given("{string} is authenticated", function (username) {
  return authenticateUser(this, username);
});

My (wip) conversion to ReScript:

@module("@cucumber/cucumber")
external given: (string, 'b) => unit = "Given"

@module("../authenticate")
external authenticateUser: ('a, string) => Js.Promise.t<unit> = "authenticateUser"

given("{string} is authenticated", (~userName: string) : Js.Promise.t<unit> => {
  authenticate(@this, userName)
})

What I don’t understand is how I should deal with ‘this’ in ReScript. What I want to achieve is the example code in JS above. Where ‘this’ is being passed to the authenticateUser function.

I’ve read Bind to JS Function | ReScript Language Manual but I’m struggling to understand how to apply that to my code. Especially since I don’t know what will be the caller of this function.

Many thanks in advance, keep up the good work! :100:

I suspect in this case you may need to use %raw().

E.g.

authenticateUser(%raw(`this`), userName)
2 Likes

There’s also a dedicated ReScript feature to support this, although it’s not often used: Bind to JS Function | ReScript Language Manual

In your case it would look like:

module World = {
  type t
}

@module("@cucumber/cucumber")
external given1: (string, @this ((World.t, 'a1) => unit)) => unit = "Given"

given1("{string} is authenticated", @this ((world, username) => {
  Js.log(world) // this
  assert(username == "Bla")
}))

The idea is that the this argument is explicitly passed in as the first parameter in ReScript, but it’s stripped out from the compiled JS output and instead directly referenced as this.

4 Likes

@kevanstannard @yawaramin Thanks a lot for helping out!

Eventually, I got it to work using:

@module("../authenticate")
external authenticateUser: (World.t, string) => Js.Promise.t<unit> = "authenticateUser"

@module("@cucumber/cucumber")
external given: (string, @this (World.t, string) => Js.Promise.t<unit>) => unit = "Given"

given("{string} is authenticated", @this (world, username): Js.Promise.t<unit> => {
  authenticate(world, username)
})
2 Likes