Since kotlinjs documentation says it has interop w...
# javascript
c
Since kotlinjs documentation says it has interop with Javascript and the other way around I'm wondering if someone could share some experiences of having a mixed project including ts and kotlin files. For my usecase I have a react app (created with create-react-app) written in typescript and want to migrate it to Kotlin step by step. Is this possible and how would I have to set it up? I searched the web but couldn't find something beside this discussion that describes exactly my usecase but isn't answered https://discuss.kotlinlang.org/t/incremental-migration-of-typescript-projects-to-kotlin/13416
a
I have just spent time spiking exactly this. My conclusion is that it is doable, but it is clunky as hell, due to the very raw/alpha nature of the tech. There are many bugs that require workarounds. I have posted about this on this channel recently.
What I did was write a client lib that invokes an HTTP/JSON API. My goal was to have the types sent across the wire implemented in Kotlin common, so we didn't have to duplicate the code across our JVM, JS and TypeScript clients. I used `expect`/`actual` to delegate the actual HTTP calls to platform-specific libs. This worked pretty well, once I worked out that the
external
declarations generated by kotlin need a bit of massaging to be useful. I wrote a simple unit test in TypeScript, using jest, which depended on the client I had written. This is where the fun starts. 1. You cannot
@JsExport
a class that is
@Serializable
using kotlinx-serialization. 2. A non-`@JsExport`'ed class that implements a `@JsExport`'ed interface is unusable (from typescrtip) as its members are all prefixed with
_
, while those of the interface are not. 3. The combination of 1 & 2 means that you must duplication any class that you want to be part of your interface - one copy to serialize across the wire and one to actually use in your typescript. 4. The TypeScript .d.ts files generated by the IR compiler put all your code in a namespace matching your Kotlin package name, which is clunky to import from TypeScript. What we ended up with is something like:
Copy code
import my from "my-module"; 
import my.package.MyClass = MyClass;
There are also plenty of Kotlin code constructs that are not correctly represented in TS. 1. Any JS/TS code that you call from Kotlin (via an
external
declaration) does not get properly converted back to TypeScript when you
@JsExport
it. The .d.ts file references the kotlin declaration and not the JS/TS code. This means that any function that returns a Promise will return a
kotlin.js.Promise
and not a javascript
Promise
, which is pretty meaningless in JS/TS and breaks async/await. You can work around this by returning
dynamic
and casting the returned object, but that starts to discard some of the benefit of TypeScript. 2. A kotlin function with an expression body will cause the IR compiler to fail. Convert it to a block body and you're ok. 3. A kotlin function with a trailing line comment will cause the IR compiler to fail. 4. JsExporting classes with nested classes will cause the IR compiler to fail. 5. JsExporting classes with companion objects will cause the IR compiler to fail. 6. JsExporting a data class will leave you with pointless
hashCode()
,
equals()
and
componentN()
methods in your typescript defintions.
👍 1
That said, it can be made to work, if you are willing to work through all of these issues. I suspect you will have an easier time of it if you wait fro these bugs to be fixed.
👍 1
c
thx now knowing this i think i don’t want to have it that painful and wait until these bugs are fixed^^
a
quitter! 😉
😆 2
Oh, and one other thing I would add: Kotlin collections have no useful representation in typescript. Any List that you want to
@JsExport
should use an Array instead. Other collection types, you will need to figure out the trade off that works best for you.