Why are there so many restrictions on JS export?

I thought I had understood the concepts of Kotlin multiplatform and the hierarchy of source sets:

  • Code that can be run on every platform goes into commonMain, code that can only be run in the JVM goes into jvmMain, code that can only be run on a JavaScript engine goes into jsMain.
  • Also, declarations for which there are platform-specific implementations goes into commonMain, while the implementations go into jvmMain and jsMain.
  • A large part of the Kotlin standard library can be used from commonMain because there are implementations for JVM and JS.

But now that I am trying to make my multiplatform library accessible from JavaScript/TypeScript, I’m confused. Why is it that for a multiplatform library to be usable from JS code…

  • every declaration must be annotated with @JsExport (assuming the IR backend)? Of course if it’s public I want to export it - otherwise I would have put it in jvmMain etc., not commonMain!
  • even simple types like kotlin.collections.List are not supported? There must be a JS implementation in the standard library, so why can it not be used? My library should get a dependency on the Kotlin JS standard library (or have the used parts baked in by Webpack), then the consuming JavaScript code has access to it as well, right?

These restrictions seem to undermine the elegant multiplatform model. Can someone please explain and also give a perspective on the future of these issues?

Update:

Now I’m even more baffled, because this works in a regular TypeScript project:

# Install the official Kotlin standard library
npm install -save kotlin@1.6.10

# Install TypeScript definitions provided by a third party,
# because for some unknown reason, the standard library 
# doesn't provide them
npm install -save @mtm-solutions/kotlin-stdlib-wrapper@1.1.3
import * as kotlinStdlibWrapper from "@mtm-solutions/kotlin-stdlib-wrapper"

type List<T> = kotlinStdlibWrapper.kotlin.collections.List<T>
const listOf = kotlinStdlibWrapper.kotlin.collections.listOf

const list: List<string> = listOf(["a", "b"])
console.log(list.get(1)) // "b"

So it is obviously possible to use the List type from the standard library from a JavaScript/TypeScript project, yet Kotlin multiplatform disallows exporting declarations that use it.

Cross-posted on Reddit.

1 Like

Not sure why no one replied to this; @JsExport means that something is available from JS code—the Kotlin compiler cannot see it, and therefore it forbids most optimizations the compiler could otherwise apply (e.g. renaming functions to minimize the bundle, remove functions entirely, etc).

For example, if LinkedList was marked @JsExport, even if your project never used it, the compiler would still be forced to keep it in the final bundle. This is why almost nothing from the standard library is declared as @JsExport; it may be usable from JS as it is declared, but when you build your app for production, at that point it may be removed and not be usable anymore.

1 Like