Spec-Zone.ru › Kotlin 1.9

Миграция проектов Kotlin/JS на компилятор IR

Мы заменили старый компилятор Kotlin/JS на компилятор на основе IR, чтобы унифицировать поведение Kotlin на всех платформах и сделать возможной реализацию новых JS-специфичных оптимизаций, среди прочего. Вы можете узнать больше о внутренних различиях между двумя компиляторами в статье блога Миграция нашего приложения Kotlin/JS на новый компилятор IR от Себастьяна Айгнера.

Из-за значительных различий между компиляторами, переключение вашего проекта Kotlin/JS со старого бэкенда на новый может потребовать корректировки кода. На этой странице мы собрали список известных проблем миграции вместе с предлагаемыми решениями.

Установите плагин Kotlin/JS Inspection pack, чтобы получить ценные советы по исправлению некоторых проблем, возникающих во время миграции.

Обратите внимание, что это руководство может со временем изменяться по мере исправления проблем и обнаружения новых. Помогите нам поддерживать его полноту — сообщите о любых проблемах, с которыми вы столкнулись при переходе на компилятор 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:

  • Шаблон для State

  • Шаблон для Props

Преобразование свойств внешних интерфейсов в 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

Сделать булевы свойства необязательными во внешних интерфейсах

Проблема: когда вы вызываете 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 внешних интерфейсов необязательными (Boolean?):

// Replace this
external interface SomeExternal {
    var visible: Boolean
}
// With this
external interface SomeExternal {
    var visible: Boolean?
}
Последнее изменение: 11 мая 2023 г.
Компилятор Kotlin/JS IR API браузера и DOM

© 2010–2023 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API