ankushg
10/05/2021, 1:01 AMexternal interface
s for `data class`es when working in a multiplatform project with JS targets?
What about generating/maintaining the methods to map between the two?
Context/Problem:
We've written some `external interface`s for JS interop for methods that use simpler classes, but for complicated, nested, interconnected classes, it's a lot of effort to maintain this code.
We're currently using kotlinx.serialization
and encodeToDynamic
/`encodeFromDynamic` to handle those complicated types. The developer experience within KMP when using kotlinx.serialization
is much nicer, but:
1. we get dynamic
types back instead of rich external interface
s, so we don't get typescript definitions
2. serialization's json artifact is extremely heavyweight for this on web because it pulls in full JSON support for strings and JsonElement trees when we really just need lightweight methods that create and return plain JS objects as described here
Potential Solution?
It seems like those two problems above might be separately solvable?
1. Could be solved with an annotation processor to generate `external interface`s for classes annotated with @Serializable
(or some other annotation) (which we can then cast the serialization-generated `dynamic`s to/from)
2. Can maybe be solved with an alternative serialization format that is
a. only usable by the js
target,
b. operates directly on JS `Object`s, and
c. excludes all the heavy stuff for string or JsonElements in the official json
format
If this sounds like stuff that anyone else is working on or running up against, I'd love to chat!andylamax
10/05/2021, 1:48 AMankushg
10/05/2021, 2:01 AMGrégory Lureau
10/05/2021, 7:14 AM@Export // My annotation
enum class MediaFileSource {
SYNCHRO_CACHE,
SMART_CACHE,
REMOTE
}
will generate
import com.[...].MediaFileSource as CommonMediaFileSource
@JsExport
public class MediaFileSource internal constructor(internal val `value`: CommonMediaFileSource) {
public val name: String = value.name
}
public fun MediaFileSource.`import`() = value
public fun CommonMediaFileSource.export() = MediaFileSource(this)
@JsExport
public object MediaFileSources {
public val SYNCHRO_CACHE: MediaFileSource = CommonMediaFileSource.SYNCHRO_CACHE.export()
public val SMART_CACHE: MediaFileSource = CommonMediaFileSource.SMART_CACHE.export()
public val REMOTE: MediaFileSource = CommonMediaFileSource.REMOTE.export()
}
So it looks like a bit better for our web team (they use typescript & js). I also work on classes and interfaces (PM if you want more examples, a bit verbose).
No serialization here but it provides wrapper in both ways (JS <-> common Kotlin code), and since it's generated, the coupling is also generated (if a class contains a type that needs to be imported/exported, it will assume there will be a generated code for this class too, so that I don't care if my instance is complex or not, I just need to call import/export method).
It's not yet open source but I thought it could maybe help you? (I mean at least some part could be copy/paste đŸ™‚)