Calling JavaScript from Kotlin
Calling JavaScript from Kotlin
Kotlin was designed for easy interoperation with Java platform. It sees Java classes as Kotlin classes, and Java sees Kotlin classes as Java classes. However, JavaScript is a dynamically-typed language, which means it does not check types in compile-time. You can freely talk to JavaScript from Kotlin via dynamic types, but if you want the full power of Kotlin type system, you can create Kotlin headers for JavaScript libraries.
Inline JavaScript
You can inline some JavaScript code into your Kotlin code using the js("…") function. For example:
fun jsTypeOf(o: Any): String { return js("typeof o") }
The parameter of js
is required to be a string constant. So, the following code is incorrect:
fun jsTypeOf(o: Any): String { return js(getTypeof() + " o") // error reported here } fun getTypeof() = "typeof"
external
modifier
To tell Kotlin that a certain declaration is written in pure JavaScript, you should mark it with external
modifier. When the compiler sees such a declaration, it assumes that the implementation for the corresponding class, function or property is provided by the developer, and therefore does not try to generate any JavaScript code from the declaration. This means that you should omit bodies of external
declarations. For example:
external fun alert(message: Any?): Unit external class Node { val firstChild: Node fun append(child: Node): Node fun removeChild(child: Node): Node // etc } external val window: Window
Note that external
modifier is inherited by nested declarations, i.e. in Node
class we do not put external
before member functions and properties.
The external
modifier is only allowed on package-level declarations. You can't declare an external
member of a non-external
class.
Declaring (static) members of a class
In JavaScript you can define members either on a prototype or a class itself. I.e.:
function MyClass() { } MyClass.sharedMember = function() { /* implementation */ }; MyClass.prototype.ownMember = function() { /* implementation */ };
There's no such syntax in Kotlin. However, in Kotlin we have companion
objects. Kotlin treats companion objects of external
class in a special way: instead of expecting an object, it assumes members of companion objects to be members of the class itself. To describe MyClass
from the example above, you can write:
external class MyClass { companion object { fun sharedMember() } fun ownMember() }
Declaring optional parameters
An external function can have optional parameters. How the JavaScript implementation actually computes default values for these parameters, is unknown to Kotlin, thus it's impossible to use the usual syntax to declare such parameters in Kotlin. You should use the following syntax:
external fun myFunWithOptionalArgs(x: Int, y: String = definedExternally, z: Long = definedExternally)
This means you can call myFunWithOptionalArgs
with one required argument and two optional arguments (their default values are calculated by some JavaScript code).
Extending JavaScript classes
You can easily extend JavaScript classes as they were Kotlin classes. Just define an external
class and extend it by non-external
class. For example:
external open class HTMLElement : Element() { /* members */ } class CustomElement : HTMLElement() { fun foo() { alert("bar") } }
There are some limitations:
- When a function of external base class is overloaded by signature, you can't override it in a derived class.
- You can't override a function with default arguments.
Note that you can't extend a non-external class by external classes.
external
interfaces
JavaScript does not have the concept of interfaces. When a function expects its parameter to support foo
and bar
methods, you just pass objects that actually have these methods. You can use interfaces to express this for statically-typed Kotlin, for example:
external interface HasFooAndBar { fun foo() fun bar() } external fun myFunction(p: HasFooAndBar)
Another use case for external interfaces is to describe settings objects. For example:
external interface JQueryAjaxSettings { var async: Boolean var cache: Boolean var complete: (JQueryXHR, String) -> Unit // etc } fun JQueryAjaxSettings(): JQueryAjaxSettings = js("{}") external class JQuery { companion object { fun get(settings: JQueryAjaxSettings): JQueryXHR } } fun sendQuery() { JQuery.get(JQueryAjaxSettings().apply { complete = { (xhr, data) -> window.alert("Request complete") } }) }
External interfaces have some restrictions:
- They can't be used on the right hand side of
is
checks. -
as
cast to external interface always succeeds (and produces a warning in compile-time). - They can't be passed as reified type arguments.
- Then can't be used in class literal expression (i.e.
I::class
).
© 2010–2017 JetBrains s.r.o.
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/reference/js-interop.html