Skip to content

Instantly share code, notes, and snippets.

@jbhoot
Last active December 19, 2022 16:53
Show Gist options
  • Save jbhoot/db87533e7741b756d48dc2b5bed91a33 to your computer and use it in GitHub Desktop.
Save jbhoot/db87533e7741b756d48dc2b5bed91a33 to your computer and use it in GitHub Desktop.
Experiments with typing event handlers
@val external document: Dom.document = "document"
@val external window: Dom.window = "window"
type opts = {useCapture: bool}
type ev<'t, 'ct> = {
target: 't,
currentTarget: 'ct,
}
module FileReader = {
type t
@new external make: unit => t = "FileReader"
}
module AbortSignal = {
type t
@new external make: unit => t = "AbortSignal"
}
module Approach1 = {
@send
external listen_to_click: (
'ct,
@as("click") _,
ev<'t, 'ct> => unit,
option<opts>,
) => unit = "addEventListener"
@send
external listen_to_dblclick: (
'ct,
@as("dblclick") _,
ev<'t, 'ct> => unit,
option<opts>,
) => unit = "addEventListener"
let eg1 = listen_to_click(
document,
(event: ev<Dom.element, Dom.document>) => {
let tgt = event.target
let ctgt = event.currentTarget
},
None,
)
let eg2 = listen_to_dblclick(
window,
(event: ev<Dom.element, Dom.window>) => {
let tgt = event.target
let ctgt = event.currentTarget
},
None,
)
}
module Approach2 = {
@send
external listen: (
'ct,
@string
[
| #click(ev<'t, 'ct> => unit)
| #dblclick(ev<'t, 'ct> => unit)
| #abort(ev<FileReader.t, FileReader.t> => unit)
| @as("abort") #abort2(ev<AbortSignal.t, AbortSignal.t> => unit)
],
option<opts>,
) => unit = "addEventListener"
let eg1 = listen(
document,
#click(
(event: ev<Dom.element, Dom.document>) => {
let tgt = event.target
let ctgt = event.currentTarget
},
),
None,
)
let eg2 = listen(
window,
#dblclick(
(event: ev<Dom.element, Dom.window>) => {
let tgt = event.target
let ctgt = event.currentTarget
},
),
None,
)
let eg3 = listen(
AbortSignal.make(),
#abort2(
event => {
let tgt = event.target
let ctgt = event.currentTarget
},
),
None,
)
}
module Approach3 = {
type name = [#click | #dblclick]
@send
external listen_to_mouse_ev: (
'ct,
name,
ev<'t, 'ct> => unit,
option<opts>,
) => unit = "addEventListener"
let eg1 = listen_to_mouse_ev(
document,
#click,
(event: ev<Dom.element, Dom.document>) => {
let tgt = event.target
let ctgt = event.currentTarget
},
None,
)
let eg2 = listen_to_mouse_ev(
document,
#dblclick,
(event: ev<Dom.element, Dom.document>) => {
let tgt = event.target
let ctgt = event.currentTarget
},
None,
)
}
external document : Dom.document = "document" [@@val]
external window : Dom.window = "window" [@@val]
module FileReader = struct
type t
external make : unit -> t = "FileReader" [@@bs.new]
end
module AbortSignal = struct
type t
external make : unit -> t = "AbortSignal" [@@bs.new]
end
type opts = { useCapture : bool }
type ('t, 'ct) ev =
{ target : 't
; currentTarget : 'ct
}
module Approach1 = struct
external listen_to_click :
'ct -> (_[@as "click"]) -> (('t, 'ct) ev -> unit) -> opts option -> unit
= "addEventListener"
[@@send]
external listen_to_dblclick :
'ct -> (_[@as "dblclick"]) -> (('t, 'ct) ev -> unit) -> opts option -> unit
= "addEventListener"
[@@send]
let eg1 =
listen_to_click document
(fun (event : (Dom.element, Dom.document) ev) ->
let tgt = event.target in
let ctgt = event.currentTarget in
())
None
let eg2 =
listen_to_dblclick window
(fun (event : (Dom.element, Dom.window) ev) ->
let tgt = event.target in
let ctgt = event.currentTarget in
())
None
end
module Approach2 = struct
(* NOTE: The polymorphic variant MUST BE "inlined". The following code does
not work. *)
(* type ('t, 'ct) name = *)
(* ([ `click of ('t, 'ct) ev -> unit *)
(* | `dblclick of ('t, 'ct) ev -> unit *)
(* ] *)
(* [@string]) *)
(* external listen : 'ct -> ('t, 'ct) name -> opts option -> unit *)
(* = "addEventListener" *)
(* [@@send] *)
(* It compiles to: *)
(* document.addEventListener({ *)
(* NAME: "click", *)
(* VAL: (function ($$event) { *)
(* }) *)
(* }, undefined); *)
external listen :
'ct
-> ([ `click of ('t, 'ct) ev -> unit
| `dblclick of ('t, 'ct) ev -> unit
| `abort_abortsignal of (AbortSignal.t, AbortSignal.t) ev -> unit
[@as "abort"]
| `abort_filereader of (FileReader.t, FileReader.t) ev -> unit
[@as "abort"]
]
[@string])
-> opts option
-> unit = "addEventListener"
[@@send]
let eg1 =
listen document
(`click
(fun (event : (Dom.element, Dom.document) ev) ->
let tgt = event.target in
let ctgt = event.currentTarget in
()))
None
let eg2 =
listen window
(`dblclick
(fun (event : (Dom.element, Dom.window) ev) ->
let tgt = event.target in
let ctgt = event.currentTarget in
()))
None
let eg3 =
listen (AbortSignal.make ())
(`abort_abortsignal
(fun event ->
let tgt = event.target in
let ctgt = event.currentTarget in
()))
None
end
module Approach3 = struct
type name =
[ `click
| `dblclick
]
external listen_to_mouse_ev :
'ct -> name -> (('t, 'ct) ev -> unit) -> opts option -> unit
= "addEventListener"
[@@send]
let eg1 =
listen_to_mouse_ev document `click
(fun (event : (Dom.element, Dom.document) ev) ->
let tgt = event.target in
let ctgt = event.currentTarget in
())
None
let eg2 =
listen_to_mouse_ev document `dblclick
(fun (event : (Dom.element, Dom.document) ev) ->
let tgt = event.target in
let ctgt = event.currentTarget in
())
None
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment