I’m trying to wrap an external Svelte class, Store
, defined in store.js.
My initial class looks like this:
external class Store(initialData: Map<String, Any?> = definedExternally) {
fun get(): Map<String, Any?>
fun set(obj: Map<String, Any?>)
// snip
}
The constructor accepts a map, assigns it to an internal field, which can then be updated using set
and retrieved using get
. (get
returns the entire thing, while set
merges in the new map into the current state).
Suppose I construct the Store like this:
Store(mapOf("currentPage" to "vis"))
And then I pass it in to my other Svelte component like this:
App(svelteOptions(store = store))
// elsewhere
inline fun svelteOptions(store: Store? = null): SvelteOptions {
val o = js("({})")
store?.let { o["store"] = it }
return o.unsafeCast<SvelteOptions>()
}
Finally, off in my SideNavLink.html
Svelte component (which is not Kotlin), I do this:
this.store.set({ currentPage: name });
Something surprising happens when, back in my Kotlin code, I have this:
store.onState { (changed) ->
if (changed.currentPage) {
console.log("Current page changed! is now ${store.get()["currentPage"]}")
}
}
The surprising thing: store.get()["currentPage"]
always has the initial value that it was given in the constructor!!
This took me a little while to figure out, but the reason is this: I’m constructing the Store with an actual Kotlin Map object. When I call .set()
from my non-Kotlin Svelte component, it’s attaching a property directly to this Map object. But then when I call .get()["whatever"]
, it’s not looking up the whatever
property – it’s executing .get().get_11rb$(key)
– looking up the key in the Kotlin Map, not whatever was set on the object itself.
There is a solution to this: change fun get(): Map<String, Any?>
to fun get(): dynamic
. But it IS a Map-shaped Object, just not a Kotlin/JVM map. Is there a solution here that doesn’t involve plastering dynamic
all over my Store
external class?