Использование кода JavaScript в Kotlin
Kotlin изначально был разработан для простой совместимости с платформой Java: он рассматривает классы Java как классы Kotlin, а Java рассматривает классы Kotlin как классы Java.
Однако JavaScript — это язык с динамической типизацией, то есть он не проверяет типы во время компиляции. Вы можете свободно взаимодействовать с JavaScript из Kotlin с помощью типов dynamic. Если вы хотите использовать всю мощь системы типов Kotlin, можно создать внешние объявления для библиотек JavaScript, которые будут распознаны компилятором Kotlin и окружающими его инструментами.
Встраивание JavaScript
Вы можете встроить код JavaScript в код Kotlin с помощью функции js():
fun jsTypeOf(o: Any): String {
return js("typeof o")
}
Встраивание кода JavaScript полностью поддерживает возможности ES2015, включая:
объявления переменных
constиletКлассы ES
Генераторы
Лямбда-выражения (стрелочные функции)
Операторы spread и rest
Шаблонные строки
Поскольку параметр функции js анализируется во время компиляции и переносится в код JavaScript «как есть», он должен быть строковой константой. Поэтому следующий код некорректен:
fun jsTypeOf(o: Any): String {
return js(getTypeof() + " o") // Error: Argument must be a string constant
// The compiler cannot evaluate string concatenations
}
fun getTypeof() = "typeof"
Вместо этого, например, чтобы встроить оператор rest, используйте строковую константу:
fun runSumExample() {
val sum = js("(...nums) => nums.reduce((a, b) => a + b, 0)")
println(sum(1, 2, 3, 4))
}
Модификатор external
Чтобы сообщить Kotlin, что определённое объявление написано на чистом JavaScript, пометьте его модификатором external. Когда компилятор встречает такое объявление, он предполагает, что реализация соответствующего класса, функции или свойства предоставляется извне (разработчиком или через зависимость npm), и поэтому не пытается генерировать код JavaScript на основе этого объявления. Именно поэтому объявления external не могут иметь тело. Например:
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
Обратите внимание, что модификатор external наследуется вложенными объявлениями. Поэтому в примере с классом Node перед функциями-членами и свойствами не указан модификатор external.
Модификатор external разрешён только для объявлений на уровне пакета. Нельзя объявлять член external в классе, не являющемся external.
Объявление (статических) членов класса
В JavaScript можно определять члены либо в прототипе, либо непосредственно в самом классе:
function MyClass() { ... }
MyClass.sharedMember = function() { /* implementation */ };
MyClass.prototype.ownMember = function() { /* implementation */ };
В Kotlin такого синтаксиса нет. Однако в Kotlin есть объекты companion. Kotlin особым образом обрабатывает объекты-компаньоны классов external: вместо того чтобы ожидать объект, он рассматривает члены объектов-компаньонов как члены самого класса. MyClass из примера выше можно описать так:
external class MyClass {
companion object {
fun sharedMember()
}
fun ownMember()
}
Объявление параметров со значениями по умолчанию
Если вы пишете внешнее объявление для функции JavaScript с параметром, имеющим значение по умолчанию, используйте definedExternally. Это позволяет передать вычисление значений по умолчанию самой функции JavaScript:
external fun myFunWithOptionalArgs(
x: Int,
y: String = definedExternally,
z: String = definedExternally
)
С таким внешним объявлением можно вызывать myFunWithOptionalArgs с одним обязательным и двумя необязательными аргументами. Значения по умолчанию будут вычислены реализацией myFunWithOptionalArgs на JavaScript.
Наследование от классов JavaScript
Вы можете с лёгкостью наследоваться от классов JavaScript так же, как от классов Kotlin. Просто объявите класс external open и унаследуйте от него класс, не являющийся external. Например:
open external class Foo {
open fun run()
fun stop()
}
class Bar : Foo() {
override fun run() {
window.alert("Running!")
}
fun restart() {
window.alert("Restarting")
}
}
Есть несколько ограничений:
Если функция внешнего базового класса перегружена по сигнатуре, переопределить её в производном классе нельзя.
Нельзя переопределять функцию, содержащую параметры со значениями по умолчанию.
От внешних классов нельзя наследоваться в невнешних классах.
Внешние интерфейсы
В JavaScript нет понятия интерфейсов. Если функция ожидает, что её параметр поддерживает два метода foo и bar, достаточно передать объект, у которого действительно есть эти методы.
В статически типизированном Kotlin это понятие можно выразить с помощью интерфейсов:
external interface HasFooAndBar {
fun foo()
fun bar()
}
external fun myFunction(p: HasFooAndBar)
Типичный вариант использования внешних интерфейсов — описание объектов настроек. Например:
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")
}
})
}
У внешних интерфейсов есть несколько ограничений:
Их нельзя использовать в правой части проверок
is.Их нельзя передавать в качестве аргументов reified-типа.
Их нельзя использовать в выражениях с литералом класса (например,
I::class).-
Приведения
asк внешним интерфейсам всегда завершаются успешно. При приведении к внешним интерфейсам во время компиляции выводится предупреждение «Unchecked cast to external interface» (непроверяемое приведение к внешнему интерфейсу). Это предупреждение можно подавить с помощью аннотации@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE").IntelliJ IDEA также может автоматически сгенерировать аннотацию
@Suppress. Откройте меню намерений с помощью значка лампочки или сочетания Alt-Enter и нажмите маленькую стрелку рядом с проверкой «Unchecked cast to external interface». Здесь можно выбрать область подавления, и IDE соответствующим образом добавит аннотацию в файл.
Приведения типов
Помимо «небезопасного» оператора приведения типов as, который выбрасывает ClassCastException, если приведение невозможно, Kotlin/JS также предоставляет unsafeCast<T>(). При использовании unsafeCast во время выполнения вообще не выполняется проверка типов. Например, рассмотрим следующие два метода:
fun usingUnsafeCast(s: Any) = s.unsafeCast<String>() fun usingAsOperator(s: Any) = s as String
Они будут скомпилированы следующим образом:
function usingUnsafeCast(s) {
return s;
}
function usingAsOperator(s) {
var tmp$;
return typeof (tmp$ = s) === 'string' ? tmp$ : throwCCE();
}
Равенство
В Kotlin/JS проверки на равенство имеют особую семантику по сравнению с другими платформами.
В Kotlin/JS оператор ссылочного равенства в Kotlin (===) всегда преобразуется в оператор строгого равенства в JavaScript (===).
Оператор === в JavaScript проверяет не только равенство двух значений, но и совпадение их типов:
fun main() {
val name = "kotlin"
val value1 = name.substring(0, 1)
val value2 = name.substring(0, 1)
println(value1 === value2)
// Prints 'true' on Kotlin/JS
// Prints 'false' on other platforms
}
Кроме того, в Kotlin/JS числовые типы Byte, Short, Int, Float и Double во время выполнения представлены одним и тем же типом JavaScript — Number. Поэтому значения этих пяти типов неразличимы:
fun main() {
println(1.0 as Any === 1 as Any)
// Prints 'true' on Kotlin/JS
// Prints 'false' on other platforms
}
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/js-interop.html