Additionally, I found that using bare data classes...
# javascript
j
Additionally, I found that using bare data classes (consisting only of primitive types, and nested data classes, creating quite complex structures) with javascript functions expecting objects and vice versa works perfectly well:
Copy code
data class Test(val i: Int, val s: String)
var t = Test(1, "hello")
someJavascriptFunctionExpectingObject(t)
var t = js("{i: 1, s: 'hello'}").unsafeCase<Test>()
In hindsight it seems obvious that this works. But when Googling around for this I ran into all sorts of parsers and lengthy code ending up with the same thing but significantly slower. Is there something wrong with doing it this way, other than losing presence/type checks for the fields? Is this likely to break in unexpected ways (such as?) or is it fine to use in tightly controlled cases? Thank you for coming to my TED talk 🙂
a
data class Test
is a kotlin class meaning it has
constructors,copy(), toString() equals and hashcode()
while
js("{i: 1, s: 'helo'}")
is just a plain javascript object.
unsafeCast<Test>()
tells the compiler to completely ignore the underlying data type and just pretend it is an object of
class Test
while this succeeds, the operation is not safe as the name suggests. in short. The only similarity in the two object, is that they appear to have the same property names. FYI, also
js("{i:"1", s: 40}").unsafeCast<Test>()
would work.
Copy code
val test1 = js("{i: 1, s:'hello'}")
console.log(test1.i) // might work (name mangling)

console.log(test1.s) // might work

console.log(test1.i + 1) // might work

console.log(test1.copy(i=5)) // error

console.log(Test(1,"hello")==test) // false

val test2 = js("{i: 'One', s:'hello'}")
console.log(test2.i) // might work

console.log(test2.s) // might work

console.log(test2.i + 1) // undefined

console.log(test2.copy(i=5)) // error

console.log(Test(1,"hello")==test) // false (as expected)
👍 1
j
I see, thanks for pointing out these limitations! Not sure why I even tried data class, it works just as well as normal class without implying copy, toString, equals or hashcode. For my use-case - tight loops, no time for expensive conversions, IDE helping with code completion / preventing typos - it seems this will work well enough. Just Javascript primitives, no methods (though inlines also work), and that object unsafeCast back is always a browser-made exact deep-copy of an object of the same class originally provided by the Kotlin code, so the type of the value will always be compatible. The only thing of concern from the above is the name mangling, but this really only happens when subclassing/overloading, right? And there's always @JsName for that.
a
yes, @JsName actually does solve the name mangling. But you should know
unsafeCast()
doesn't do deep copy at all. you can check its imolementation. How ever, a deep copy method provided by the kotlin std lib, would be great
j
Ah yes, that's actually the point, doing this without a copy, just referencing the js object with code completion 🙂