Миграция проектов Kotlin/JS на компилятор IR
Мы заменяем текущий компилятор Kotlin/JS на компилятор на основе IR, чтобы унифицировать поведение Kotlin на всех платформах и, среди прочего, сделать возможным реализацию новых оптимизаций, специфичных для JS. Более подробную информацию о внутренних различиях между двумя компиляторами можно найти в посте в блоге Миграция приложения Kotlin/JS на новый компилятор IR от Себастьяна Айгнера.
Из-за значительных различий между компиляторами, переключение вашего проекта Kotlin/JS со старого бэкэнда на новый может потребовать корректировки кода. На этой странице мы собрали список известных проблем миграции вместе с предлагаемыми решениями.
Обратите внимание, что это руководство может меняться со временем по мере устранения проблем и обнаружения новых. Пожалуйста, помогите нам поддерживать его в актуальном состоянии – сообщайте о любых проблемах, с которыми вы столкнулись при переходе на компилятор IR, отправив их в наш трекер проблем YouTrack или заполнив эту форму.
Преобразование классов и интерфейсов, связанных с JS и React, во внешние интерфейсы
Проблема: Использование Kotlin-интерфейсов и классов (включая data-классы), которые наследуются от чистых JS-классов, таких как React's State и Props, может привести к ошибке ClassCastException. Такие исключения появляются, потому что компилятор пытается работать с экземплярами этих классов как с Kotlin-объектами, когда они фактически происходят из JS.
Решение: Преобразуйте все классы и интерфейсы, которые наследуются от чистых JS-классов, во внешние интерфейсы:
// Replace this
interface AppState : State { }
interface AppProps : Props { }
data class CustomComponentState(var name: String) : State
// With this
external interface AppState : State { }
external interface AppProps : Props { }
external interface CustomComponentState : State {
var name: String
}
В IntelliJ IDEA вы можете использовать эти шаблоны структурного поиска и замены, чтобы автоматически пометить интерфейсы как external:
Преобразование свойств внешних интерфейсов в var
Проблема: свойства внешних интерфейсов в коде Kotlin/JS не могут быть неизменяемыми (val) свойствами, потому что их значения можно назначить только после создания объекта с js() или jso() (вспомогательная функция из kotlin-wrappers):
import kotlinx.js.jso val myState = jso<CustomComponentState>() myState.name = "name"
Решение: Преобразуйте все свойства внешних интерфейсов в var:
// Replace this
external interface CustomComponentState : State {
val name: String
}
// With this
external interface CustomComponentState : State {
var name: String
}
Преобразование функций с получателями во внешних интерфейсах в обычные функции
Проблема: внешние объявления не могут содержать функции с получателями, такие как расширяющие функции или свойства с соответствующими функциональными типами.
Решение: Преобразуйте такие функции и свойства в обычные функции, добавив объект получателя в качестве аргумента:
// Replace this
external interface ButtonProps : Props {
var inside: StyledDOMBuilder<BUTTON>.() -> Unit
}
external interface ButtonProps : Props {
var inside: (StyledDOMBuilder<BUTTON>) -> Unit
}
Создание обычных JS-объектов для взаимодействия
Проблема: свойства объекта Kotlin, реализующего внешний интерфейс, не являются перечисляемыми. Это означает, что они не видны для операций, которые итерируют по свойствам объекта, например:
for (var name in obj)console.log(obj)JSON.stringify(obj)
Хотя к ним по-прежнему можно получить доступ по имени: obj.myProperty
external interface AppProps { var name: String }
data class AppPropsImpl(override var name: String) : AppProps
fun main() {
val jsApp = js("{name: 'App1'}") as AppProps // plain JS object
println("Kotlin sees: ${jsApp.name}") // "App1"
println("JSON.stringify sees:" + JSON.stringify(jsApp)) // {"name":"App1"} - OK
val ktApp = AppPropsImpl("App2") // Kotlin object
println("Kotlin sees: ${ktApp.name}") // "App2"
// JSON sees only the backing field, not the property
println("JSON.stringify sees:" + JSON.stringify(ktApp)) // {"_name_3":"App2"}
}
Решение 1: Создайте обычные JavaScript-объекты с js() или jso() (вспомогательная функция из kotlin-wrappers):
external interface AppProps { var name: String }
data class AppPropsImpl(override var name: String) : AppProps
// Replace this
val ktApp = AppPropsImpl("App1") // Kotlin object
// With this
val jsApp = js("{name: 'App1'}") as AppProps // or jso {}
Решение 2: Создайте объекты с kotlin.js.json():
// or with this
val jsonApp = kotlin.js.json(Pair("name", "App1")) as AppProps
Замена вызовов toString() на ссылки на функции с .name
Проблема: в IR-бэкенде вызов toString() на ссылки на функции не приводит к уникальным значениям.
Решение: используйте свойство name вместо toString().
Явно укажите binaries.executable() в файле сборки
Проблема: компилятор не создает исполняемые .js файлы.
Это может произойти, потому что по умолчанию компилятор создаёт исполняемые файлы JavaScript, в то время как компилятор IR требует явного указания. Подробнее см. в инструкции по настройке проекта Kotlin/JS.
Решение: добавьте строку binaries.executable() в файл проекта build.gradle(.kts).
kotlin {
js(IR) {
browser {
}
binaries.executable()
}
}
Дополнительные советы по устранению неполадок при работе с компилятором Kotlin/JS IR
Делайте булевы свойства nullable во внешних интерфейсах
Проблема: при вызове toString на Boolean из внешнего интерфейса, вы получаете ошибку типа Uncaught TypeError: Cannot read properties of undefined (reading 'toString'). JavaScript обрабатывает значения null или undefined булевой переменной как false. Если вы полагаетесь на вызов toString на Boolean, которое может быть null или undefined (например, когда ваш код вызывается из кода JavaScript, за который вы не отвечаете), будьте внимательны к этому:
external interface SomeExternal {
var visible: Boolean
}
fun main() {
val empty: SomeExternal = js("{}")
println(empty.visible.toString()) // Uncaught TypeError: Cannot read properties of undefined (reading 'toString')
}
Решение: Вы можете сделать свойства Boolean внешних интерфейсов nullable (Boolean?):
// Replace this
external interface SomeExternal {
var visible: Boolean
}
// With this
external interface SomeExternal {
var visible: Boolean?
}
© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/js-ir-migration.html