The ts version is type safe, as long as you transfer a EventArgMap type in createObserver, the bind and fire method will check the input types. But i am stucked in using rescript to create a type safe event observer
It’s not really an observer, but works similar to what you have:
// Act.res
type t<'a> = {
mutable value: 'a,
mutable maybeSubs: option<Set.t<'a => unit>>,
}
let make = initialValue => {
value: initialValue,
maybeSubs: None,
}
let get = act => act.value
let set = (act, value) => {
act.value = value
switch act.maybeSubs {
| Some(subs) => subs->Set.forEach(sub => sub(value))
| None => ()
}
}
let subscribe = (act, fn) => {
let set = switch act.maybeSubs {
| Some(subs) => subs
| None => {
let newSet = Set.make()
act.maybeSubs = Some(newSet)
newSet
}
}
set->Set.unsafeAdd(fn)->ignore
() => set->Set.unsafeDelete(fn)->ignore
}
let use = act =>
ReactExtra.useSyncExternalStore(
~subscribe=fn => act->subscribe(fn),
~getSnapshot=() => act->get,
~getServerSnapshot=() => act->get,
)
// Act.resi
type t<'a>
let make: 'a => t<'a>
let get: t<'a> => 'a
let set: (t<'a>, 'a) => unit
let subscribe: (t<'a>, 'a => unit) => unit => unit
let use: t<'a> => 'a
@DZakh Thanks for the reply, i write some code in rescript, it works like the ts version at some point.
module type Action = {
type t
}
module type Observer = {
type action
type t
let make: unit => t
let fire: (t, action) => unit
let bind: (t, action => unit, unit) => unit
}
module MakeObserver = (Act: Action): (Observer with type action = Act.t) => {
type action = Act.t
type t = {mutable cbs: array<Act.t => unit>}
let make = (): t => {
{
cbs: [],
}
}
let fire = (observer: t, arg: Act.t): unit => {
observer.cbs->Js.Array2.forEach(item => item(arg))
}
let bind = (observer: t, cb: Act.t => unit): (unit => unit) => {
observer.cbs = observer.cbs->Js.Array2.concat([cb])
() => {
observer.cbs = observer.cbs->Js.Array2.filter(item => item !== cb)
}
}
}
module OffsetObserver = MakeObserver({
type t = int
})
module RenderObserver = MakeObserver({
type t = unit
})
type barObserver = {
offset: OffsetObserver.t,
render: RenderObserver.t,
}
let barObserver = {
offset: OffsetObserver.make(),
render: RenderObserver.make(),
}
let v = barObserver.offset->OffsetObserver.bind(v => {
//
assert false
})
let v1 = barObserver.render->RenderObserver.bind(v => {
//
assert false
})