Hey, is it expected that I still get mangled names...
# javascript
b
Hey, is it expected that I still get mangled names when using
@JsExport
? E.g.:
Copy code
@JsExport
interface Settings {
  val syncEnabled: Boolean
  val syncItems: Array<SyncItem>
}

@JsExport
data class SettingsImpl(
  override val syncEnabled: Boolean,
  override val syncItems: Array<SyncItem>,
): Settings
results in JS code looking like this:
Copy code
function SettingsImpl(syncEnabled, syncItems) {
    this.syncEnabled_1 = syncEnabled;
    this.syncItems_1 = syncItems;
  }
(Also generally speaking, I'm curious to know the reason for these renames?)
e
all names need to be mangled because JS doesn't have overloads (adding a
fun syncEnabled()
later should not change existing names)
πŸ‘ 1
there is @JsName to give things specific names when exporting
b
but shouldn't
@JsExport
work here?
or maybe I'm missing what it's supposed to do
e
Copy code
// v1
@JsExport
interface Settings {
  val syncEnabled: Boolean
}
Copy code
// v2
@JsExport
interface Settings {
  val syncEnabled: Boolean
  fun syncEnabled(enable: Boolean)
}
I guess that's an ABI change in some respects, but if it's not being subclassed from other code, that should be ABI compatible
b
the doc of
@JsExport
says "Compiled module exposes declarations that are marked with this annotation without name mangling.". What am I missing?
e
I take that to mean that the name of
Settings
itself isn't mangled, and
Copy code
@JsExport
fun foo()
Copy code
@JsExport
val bar
shouldn't be either
πŸ‘ 1
b
ooh. I think it's already not mangled even without the annotation, but maybe it would be in some cases.
e
You've probably missed the parts of the JS code that assign the properly named getters to
SettingsImpl
.
b
yeah not exactly sure... I should try different cases on a very simple project with just one class and compare the JS with/without the annotation. On my huge project it's hard to comprehend πŸ™‚
e
I can check now for you, but this is a very basic use case and if it doesn't work it's a bug.
πŸ‘€ 1
Outputted JS
Copy code
function SettingsImpl(syncEnabled, syncItems) {
  this.n_1 = syncEnabled;
  this.o_1 = syncItems;
}

...

//region block: post-declaration
defineProp(protoOf(SettingsImpl), 'syncEnabled', function () {
  return this.l();
});
defineProp(protoOf(SettingsImpl), 'syncItems', function () {
  return this.m();
});
b
oh so the fields are mangled but not the getters, is that it?
e
Correct
b
I see! That makes sense. πŸ™ My use case is that some of these objects get "jsonified/dejsonified" so the field names are important. So I guess I'll have to use
@JsName
for that.
(I seem to have noticed that the behavior is different for private classes - but I'll have to double check)
e
@JsName
won't change the underlying field. It's always tied to the JS getter. If you want a plain JS object, you need to work with an external interface. This is what we need https://youtrack.jetbrains.com/issue/KT-17683 @Artem Kobzar do you think
JsField
will ever be implemented?
⭐ 2
b
oh interesting! But I'm sure I have a case where I have this working (maybe it's private classes then)
e
IMO you should never take for granted the fields' names. It might work in same cases, and then you introduce an intermediate interface, or a superclass, and it breaks.
e
then is it only function names that get mangled then? I still don't see how that can be avoided with overloading
b
I see πŸ€” . And when you say I could use an external interface... We usually declare
external
symbols for stuff that exist in the environment (e.g. types provided by the browser). Is it legal to declare an
external
for something that doesn't exist? (Or that exists in my code)?
e
You cannot avoid fields mangling, but getter functions will be properly generated. In K/JS there is no concept of pure exported fields (in JVM we have
@JvmField
), every call passes through an associated getter.
Is it legal to declare an
external
for something that doesn't exist
Absolutely. You just don't have to declare a
@JsModule
. The problem in this case is
external
is only available under JS source sets. If you want this feature in common code, you need https://youtrack.jetbrains.com/issue/KT-56618
b
all right! Thanks for the pointers πŸ™
e
Another approach I consider viable is augmenting the class in the K/JS backend. Example:
Copy code
@JsSerializable
@JsExport
class Example(val one: String, val two: Int)
At compile time the class is augmented with an additional function
Copy code
@JsSerializable
@JsExport
class Example(val one: String, val two: Int) {
  fun toPlainObject(): ExamplePlain { ... }
}

external interface ExamplePlain {
  val one: String
  val two: Int
}
This is only useful if you need to do stuff on the JS/TS side tho.
b
I see.
e
Overall, to me it seems that
@JsField
would solve a bunch of usability issues, included everything we just discussed. That is indeed the better solution.
πŸ‘ 1
πŸ‘πŸΎ 1
b
yes I like it!
βœ”οΈ 2