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.global##_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.

Binding a JS object

Write in .ml and in .mli:

class type my_js_type = object

  (* read only property, read value with t##prop1 *)
  method prop1 : int readonly_prop

  (* write only property, write value with t##prop2 <- 3.14 *)
  method prop2 : float writeonly_prop

  (* both read and write *)
  method prop3 : int prop

  (* method or property starting with a capital letter can be prepend
     with an underscore. *)
  method _Array : ... (* to access the JavasScript property or method Array *)

  (* Define two methods with differant types, that translate to
     the same JavaScript method. *)
  method my_fun_int : int -> unit meth
  method my_fun_string : js_string t -> unit meth
  (* Both will actually call the my_fun JavaScript method. *)

  (* To call a javascript method starting with one underscore *)
  method __hiddenfun : ..
  method __hiddenfun_ : ..
  method __hiddenfun_something : ..
  (* This will call the _hiddenfun Javascript method *)

  (* To call the javascript method '_' *)
  method __ : ..
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)