Controlling the @JsName of fields (for PouchDB interop)


#1

Hi folks,

I’ve posted a question at https://stackoverflow.com/questions/43696152/how-can-i-set-the-jsname-for-a-propertys-backing-field-in-kotlin which might be resolved if it were possible to set the @JsName of a property’s backing field – or maybe there’s some other way to do what I want.

Any ideas?

Thanks,
Hugh.


#2

As for me, classes with behavior, should not be directly put to the persistence layer. For example, such tools often do not deserialize prototypes properly, so you won’t be able to work with instances you get from the database.

Instead, these classes should be serialized first. Currently, there’s no serialization for Kotlin JS, but we are working on Kotlin multiplatform serialization. When we complete it and make it available, you’ll be able to serialize your class and put it to PouchDB.

As a workaround you can declare your class as external with all properties without implementation and with no member functions. To instantiate such class, you can use following function:

fun <T> create(): T = js("{}")

This class will be represented as a simply key-value dictionary without prototype, metadata, mangling, etc.


#3

Thanks for that, @Alexey.Andreev, that got me to a solution which works. See http://stackoverflow.com/a/43829752/320456 – let me know if you want to post the answer there yourself and have me accept it, to credit you with the SO points :slight_smile:

I indeed didn’t want any behaviour in my persistence layer, so your external class workaround is fine. I suspect that eventually my business and persistence layers will diverge enough that it doesn’t make sense to share an interface but this will do for now.

Two questions for you, which might indicate bugs. I’m using plugin and compiler version 1.1.2-2.

First, the creation code you gave

fun <T> create(): T = js("{}")

for me turns into

function jsobject() {
}

whose return value is undefined. I think this might be a bug, because the official doc recommends the shorter form, too (https://kotlinlang.org/docs/reference/js-interop.html#external-interfaces). Instead I used

fun <T> create(): T = js("{ return {}; }")

which becomes

function jsobject() {
    return {};
}

as expected.

Second, you can’t do this

fun Impl5(_id: String): Impl5 {
    return (js("{}") as Impl5).apply {
        this._id = _id
    }
}

because that explicitly inserts a type-check for Impl5, which throws ReferenceError: Impl5 is not defined (in Firefox, at least). The generic function approach skips the type-check. I’m guessing that’s an intentional difference, since you recommended it, but it seems odd.


#4

As for js function: looks like a bug, I’ll investigate this a little later. Also, don’t forget that you can mark this function as inline, so that you’ll get {} right on the call site, instead of calling simple intermediate function.

Second, you can’t do this

fun Impl5(_id: String): Impl5 {
    return (js("{}") as Impl5).apply {
        this._id = _id
    }
}

because that explicitly inserts a type-check for Impl5, which throws ReferenceError: Impl5 is not defined (in Firefox, at least).

To eliminate runtime type check, you can use unsafeCast<Impl5>() notation. It’s strange that compiler inserts type checking code for external class, this might probably be a bug. Anyway, it’s not recommended to use as for external classes and compiler should warn you about this.


#5

Okay, thanks. Let me know if you want me to raise issues or provide any more info.


#6

I just realized that I adviced to use external class. Sorry, it’s better to use external interface for this purpose. Kotlin compiler does not insert type checks for extermal interfaces, and does report warnings on as with external interfaces. Please, try this and let me know if using external interfaces is not an option in your case.


#7

Hi Alexey, external interface worked fine for me, thanks :slight_smile: