LexiFi / gen_js_api

Easy OCaml bindings for Javascript libraries
MIT License
177 stars 31 forks source link

Add support for indexers and "callable" objects #132

Closed cannorin closed 3 years ago

cannorin commented 3 years ago

This PR adds three attributes for value binding: [@@js.index_get], [@@js.index_set], and [@@js.apply].

Indexers

[@@js.index_get] and [@@js.index_set] can be used to bind to indexer in a JS object. For example, the binding for getter and setter of JS Buffer can now be written as

val get: t -> int -> int option [@@js.index_get]
val set: t -> int -> int -> unit [@@js.index_set]

instead of

val get: t -> int -> int option[@@js.custom
  let get buf index = [%js.to:int option](Ojs.array_get buf index)
    ]
val set: t -> int -> int -> unit[@@js.custom
  let set buf index value =
    Ojs.array_set buf index ([%js.of:int] value)
  ]

Function Objects

Sometimes a JS object can also be a function (so-called "callable object").

var foo = function() { .. }
foo.bar = 42

We can now bind this kind of object with the following:

module Foo : sig
  type t = private Ojs.t

  val foo: t -> int [@@js.get "foo"]
  val apply: t -> unit -> unit [@@js.apply]
end

Compatibility with TypeScript

This complements gen_js_api's ability to bind to TypeScript interfaces. With this PR merged, we now support Function Types and Indexable Types (which were very hard to bind).

interface MapLike<T> {
    [index: string]: T
}
interface SearchFunc {
    (source: string, subString: string): boolean;
}
module MapLike : sig
  type 'a t = private Ojs.t
  val get: 'a t -> string -> 'a option [@@js.index_get]
  val set: 'a t -> string -> 'a -> unit [@@js.index_set]
end
module SearchFunc : sig
  type t = private Ojs.t
  val apply: t -> string -> string -> bool [@@js.apply]
end
mlasson commented 3 years ago

Nice PR thanks !