Interoperability with JavaScript
Kotlin/Wasm allows you to use both JavaScript code in Kotlin and Kotlin code in JavaScript.
As with Kotlin/JS, the Kotlin/Wasm compiler also has interoperability with JavaScript. If you are familiar with Kotlin/JS interoperability, you can notice that Kotlin/Wasm interoperability is similar. However, there are key differences to consider.
Use JavaScript code in Kotlin
Learn how to use JavaScript code in Kotlin by using external
declarations, functions with JavaScript code snippets, and the @JsModule
annotation.
External declarations
External JavaScript code is not visible in Kotlin by default. To use JavaScript code in Kotlin, you can describe its API with external
declarations.
JavaScript functions
Consider this JavaScript function:
You can declare it in Kotlin as an external
function:
External functions don't have bodies, and you can call it as a regular Kotlin function:
JavaScript properties
Consider this global JavaScript variable:
You can declare it in Kotlin using external var
or val
properties:
These properties are initialized externally. The properties can't have = value
initializers in Kotlin code.
JavaScript classes
Consider this JavaScript class:
You can use it in Kotlin as an external class:
All declarations inside the external
class are implicitly considered external.
External interfaces
You can describe the shape of a JavaScript object in Kotlin. Consider this JavaScript function and what it returns:
See how its shape can be described in Kotlin with an external interface User
type:
External interfaces don't have runtime type information and are a compile-time-only concept. Therefore, external interfaces have some restrictions compared to regular interfaces:
You can't use them on the right-hand side of
is
checks.You can't use them in class literal expressions (such as
User::class
).You can't pass them as reified type arguments.
Casting with
as
to external interfaces always succeed.
External objects
Consider these JavaScript variables holding an object:
You can use them in Kotlin as an external object:
External type hierarchy
Similar to regular classes and interfaces, you can declare external declarations to extend other external classes and implement external interfaces. However, you can't mix external and non-external declarations in the same type hierarchy.
Kotlin functions with JavaScript code
You can add a JavaScript snippet to Kotlin/Wasm code by defining a function with = js("code")
body:
If you want to run a block of JavaScript statements, surround your code inside the string with curly brackets {}
:
If you want to return an object, surround the curly brackets {}
with parentheses ()
:
Kotlin/Wasm treats calls to the js()
function in a special way, and the implementation has some restrictions:
A
js()
function call must be provided with a string literal argument.A
js()
function call must be the only expression in the function body.The
js()
function is only allowed to be called from package-level functions.The function return type must be provided explicitly.
Types are restricted, similar to
external fun
.
The Kotlin compiler puts the code string into a function in the generated JavaScript file and imports it into WebAssembly format. The Kotlin compiler doesn't verify these JavaScript snippets. If there are JavaScript syntax errors, they are reported when you run your JavaScript code.
JavaScript modules
By default, external declarations correspond to the JavaScript global scope. If you annotate a Kotlin file with the @JsModule
annotation, then all external declarations within it are imported from the specified module.
Consider this JavaScript code sample:
Use this JavaScript code in Kotlin with the @JsModule
annotation:
Array interoperability
You can copy JavaScript's JsArray<T>
into Kotlin's native Array
or List
types; likewise, you can copy these Kotlin types to JsArray<T>
.
To convert JsArray<T>
to Array<T>
or the other way around, use one of the available adapter functions.
Here's an example of conversion between generic types:
Similar adapter functions are available for converting typed arrays to their Kotlin equivalents (for example, IntArray
and Int32Array
). For detailed information and implementation, see the kotlinx-browser
repository.
Here's an example of conversion between typed arrays:
Use Kotlin code in JavaScript
Learn how to use your Kotlin code in JavaScript by using the @JsExport
annotation.
Functions with the @JsExport annotation
To make a Kotlin/Wasm function available to JavaScript code, use the @JsExport
annotation:
Kotlin/Wasm functions marked with the @JsExport
annotation are visible as properties on a default
export of the generated .mjs
module. You can then use this function in JavaScript:
The Kotlin/Wasm compiler is capable of generating TypeScript definitions from any @JsExport
declarations in your Kotlin code. These definitions can be used by IDEs and JavaScript tools to provide code autocompletion, help with type-checks, and make it easier to consume Kotlin code from JavaScript and TypeScript.
The Kotlin/Wasm compiler collects any top-level functions marked with the @JsExport
annotation and automatically generates TypeScript definitions in a .d.ts
file.
To generate TypeScript definitions, in your build.gradle.kts
file in the wasmJs{}
block, add the generateTypeScriptDefinitions()
function:
Type correspondence
Kotlin/Wasm allows only certain types in signatures of JavaScript interop declarations. These limitations apply uniformly to declarations with external
, = js("code")
or @JsExport
.
See how Kotlin types correspond to Javascript types:
Kotlin | JavaScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Function type, for example | Function |
| Any JavaScript value |
| Opaque reference to Kotlin object |
Other types | Not supported |
You can use nullable versions of these types as well.
JsAny type
JavaScript values are represented in Kotlin using the JsAny
type and its subtypes.
The Kotlin/Wasm standard library provides representation for some of these types:
Package
kotlin.js
:JsAny
JsBoolean
,JsNumber
,JsString
JsArray
Promise
You can also create custom JsAny
subtypes by declaring an external
interface or class.
JsReference type
Kotlin values can be passed to JavaScript as opaque references using the JsReference
type.
For example, if you want to expose this Kotlin class User
to JavaScript:
You can use the toJsReference()
function to create JsReference<User>
and return it to JavaScript:
These references aren't directly available in JavaScript and behave like empty frozen JavaScript objects. To operate on these objects, you need to export more functions to JavaScript using the get()
method where you unwrap the reference value:
You can create a class and change its name from JavaScript:
Type parameters
JavaScript interop declarations can have type parameters if they have an upper bound of JsAny
or its subtypes. For example:
Exception handling
You can use Kotlin try-catch
expression to catch JavaScript exceptions. However, accessing specific details about the thrown value in Kotlin/Wasm isn’t possible by default.
You can configure the JsException
type to include the original error message and stack trace from JavaScript. To do so, add the following compiler option to your build.gradle.kts
file:
This behavior depends on the WebAssembly.JSTag
API, which is only available in certain browsers:
Chrome: Supported from version 115
Firefox: Supported from version 129
Safari: Not yet supported
Here’s an example demonstrating this behavior:
With the -Xwasm-attach-js-exception
compiler option enabled, the JsException
type provides specific details from the JavaScript error. Without enabling this compiler option, JsException
includes only a generic message stating that an exception was thrown while running JavaScript code.
If you try to use a JavaScript try-catch
expression to catch Kotlin/Wasm exceptions, it looks like a generic WebAssembly.Exception
without directly accessible messages and data.
Kotlin/Wasm and Kotlin/JS interoperability differences
Although Kotlin/Wasm interoperability shares similarities with Kotlin/JS interoperability, there are key differences to consider:
Kotlin/Wasm | Kotlin/JS | |
---|---|---|
External enums | Doesn't support external enum classes. | Supports external enum classes. |
Type extensions | Doesn't support non-external types to extend external types. | Supports non-external types. |
| Only has an effect when annotating external declarations. | Can be used to change names of regular non-external declarations. |
|
| The |
Module systems | Supports ES modules only. There is no analog of the | Supports ES modules and legacy module systems. Provides named ESM exports. Allows exporting classes and objects. |
Types | Applies stricter type restrictions uniformly to all interop declarations | Allows all types in |
Long | Type corresponds to JavaScript | Visible as a custom class in JavaScript. |
Arrays | Not supported in interop directly yet. You can use the new | Implemented as JavaScript arrays. |
Other types | Requires | Allows the use of non-external Kotlin class types in external declarations. |
Exception handling | You can catch any JavaScript exception with the | Can catch JavaScript |
Dynamic types | Does not support the | Supports the |
Web-related browser APIs
The kotlinx-browser
library is a standalone library that provides JavaScript browser APIs, including:
Package
org.khronos.webgl
:Typed arrays, like
Int8Array
.WebGL types.
Packages
org.w3c.dom.*
:DOM API types.
Package
kotlinx.browser
:DOM API global objects, like
window
anddocument
.
To use the declarations from the kotlinx-browser
library, add it as a dependency in your project's build configuration file: