How to bind a JS library for OCaml

Accessing a JS variable, ex: document:

Write in .ml:

let v = Js.Unsafe.variable "window.document"

Alternatively, the global object can be used. In the browser, it refers to window.

let v = Js.Unsafe.global##document

and in .mli:

val v : ... Js.t

Be careful the function Js.​Unsafe.​variable and the value Js.​Unsafe.​global are not typed. Verify the library documentation before writing the type.

Binding a JS function

Example from the Js module:

let decodeURI (s : js_string t) : js_string t =
  Js.Unsafe.fun_call (Js.Unsafe.variable "decodeURI") [|Js.Unsafe.inject s|]

Have a look at the Js.​Unsafe module API.

Using a JS constructor, ex: F:

Write in .ml:

let f = Js.Unsafe.variable "window.F"

and in .mli:

val f : (... -> ... Js.t) Js.constr

and if you want to use JS overloading, do, for example:

val f_fromInt : (int -> ... Js.t) Js.constr
val f_fromString : (js_string t -> ... Js.t) Js.constr
val f_blah : (#Dom_html.element t -> js_string t -> ... Js.t) Js.constr

Accessing or modifying a JS property to an element

When a property is missing in the OCaml interface of an element (for example it has been dynamically added by a library), you can access using unsafe features:

(Js.Unsafe.coerce elt)##blah

If you want to add yourself a new property:

(Js.Unsafe.coerce elt)##blah <- v

Here, v may be a JS value or an OCaml value.

If you want to do that in type safe manner, just define new types for the extended elements, or wrap the unsafe functions inside a getter and setter.

Example binding of a JS object type tt:

Write in .ml and in .mli:

class type tt = object
  method mm : int readonly_prop
  method nn : js_string t writeonly_prop
  method oo : Dom_html.element t prop
  method pp : int js_array t -> int -> js_string t opt meth
  method qq : unit meth
  method rr_fromInt : int -> float -> unit meth
  method rr_fromString : js_string t -> float -> unit meth
end

Example binding some constants:

For example if the JS class is used to define three constants thelib.Theclass.VALUEA, thelib.Theclass.VALUEB, thelib.Theclass.VALUEC,

Since ocaml doesn't allows method name to start with capitalised letter, we can add an _

write in .ml and .mli:

type thetype

class type theclass = object
  method _VALUEA : thetype readonly_prop
  method _VALUEB : thetype readonly_prop
  method _VALUEC : thetype readonly_prop
end

and in .ml:

let theclass = Js.Unsafe.variable "thelib.Theclass"

and in .mli:

val theclass : theclass t

Constructing JS objects manually

If you want to construct a JS object manually (without calling a function or a constructor), use function Js.Unsafe.obj and create the corresponding object type.

You can also create an empty object of this type (Js.Unsafe.obj [||]) and set all fields manually using o##prop <- ....

For example, write in .mli:

class type options = object
  method propOne : int writeonly_prop
  method propTwo : js_string t writeonly_prop
end

val empty_options : unit -> options t

and in the .ml file of your library:

let empty_options () = Js.Unsafe.obj [||]

Set/get variables

You can access every variable through the global javascript object (window):

If the variable var has type t Js.t

let set (x:t Js.t) = Js.Unsafe.global##var <- x
let get x : t Js.t = Js.Unsafe.global##var

Object property with multiple types

If you want to read a property of an object which can have multiple types, you can define an intermediate type to do typesafe casting ex:

Suppose the object obj has a property prop which can be either a string or a Dom node:

type dom_or_string

class type obj = object
  method prop : dom_or_string Js.t prop
end

let obj : obj Js.t = Js.Unsafe.variable "obj"

let string_constr : Js.js_string Js.t Js.constr = Js.Unsafe.global##_String

let cast_string (x:dom_or_string Js.t) : Js.js_string Js.t Js.opt =
  if instanceof x string_constr
  then Js.Unsafe.coerce x
  else Js.null

let node_constr : Dom.node Js.t Js.constr = Js.Unsafe.global##_Node

let cast_string (x:dom_or_string Js.t) : Dom.node Js.t Js.opt =
  if instanceof x node_constr
  then Js.Unsafe.coerce x
  else Js.null

Check availability of method

It is frequent that some method are not to be implemented in some browser.

To check the presence of method met:

let check_met obj = Js.Optdef.test ((Js.Unsafe.coerce obj)##met)