Search code examples
javascriptkotlinkotlin-js-interop

How to generate a javascript function with `this.field=value` in body, in KotlinJs


When I use KotlinJS with riot, it requires a function like following to define a tag:

function (opts) {
    var self = this
    self.name = "sample"
    self.message = "init-message"

    self.onCreate = fun(opts: dynamic) {
        self.message = opts.message
    }

    self.click = fun() {
        self.message = "Clicked!"
    }
}

Though I can write the Kotlin code like this:

fun(opts: dynamic) {
    val self = js("this")

    self.name = "sample"
    self.message = "init-message"

    self.onCreate = fun(opts: dynamic) {
        self.message = opts.message
    }

    self.click = fun() {
        self.message = "Clicked!"
    }
}

But you can see it has several problems:

  1. We have to use some js("this") tricks
  2. self.name, self.message, and similar, there are a lot of dynamic code all over the function body

How to avoid them? I want to write pure, type-safe Kotlin code as much as possible.

And, in advance, is it possible to define a class which has clearer structure, like:

class MyTag {
   val name = "sample"
   var message = "init-message"
   fun onCreate() {}
   fun click() {}
}

I can accept to do some conversions against the class to generate the required function.


Solution

  • The possible solution might be cast js("this") to known kotlin type. Though js() still exists its usage is very local and code actually type-safe.

    The code bellow produces what you need in js

    interface MyTag {
        var name: String
    
        var message: String
    
        var onCreate: (dynamic) -> Unit
    
        var click: () -> Unit
    }
    
    inline fun <T> builder(block: T.() -> Unit) = block(js("this"))
    
    fun tagFunction(opts: dynamic) = builder<MyTag> {
        name = "sample"
    
        message = "init-message"
    
        onCreate = { message = opts.message }
    
        click = { message = "Clicked!" }
    }