【问题标题】:How to create an "options object" (Object with many optional properties) in Kotlin compiling to JavaScript?如何在 Kotlin 编译为 JavaScript 中创建“选项对象”(具有许多可选属性的对象)?
【发布时间】:2021-03-12 03:22:07
【问题描述】:

背景:JavaScript 中的选项对象

JavaScript 中的一个常见概念是这样的:

function MyLibrary(options) {
    if (options.optionA) { /* ... */ }
    if (options.optionB) { /* ... */ }
    if (options.flagC) { /* ... */ }
}

new MyLibrary({optionA: "foo", flagC: true})

所以基本上我们有一个“选项”对象,其中包含许多可选的属性。

问题:如何与 Kotlin 进行交互?

在 Kotlin 中,我们可能更愿意使用带有默认值的命名参数。但是,我想与使用上述概念的现有 JavaScript 库进行交互。我如何描述和使用 Kotlin 中的那个库?当然,它应该尽可能地保证类型安全。

到目前为止我所尝试的

这是我想出的:

interface Options {
    var optionA: String
    var optionB: Int
    var flagC: Boolean
}

external class MyLibrary(options: Options)

fun myFunWithApply() {
    MyLibrary(Any().unsafeCast<Options>().apply {
        optionA = "foo"
        flagC = true
    })
}

这确实有效,它还为选项提供了代码完成功能,这很棒,虽然它不像原始 JavaScript 那样短,但考虑到它的长度,感觉还可以。但是...有更好的方法吗?没有unsafeCast有没有办法相处?

【问题讨论】:

    标签: javascript kotlin kotlin-js kotlin-js-interop


    【解决方案1】:

    有没有办法在没有 unsafeCast 的情况下相处?

    是的,当然。

    private external interface IOptions {
        val optionA: String
        val optionB: Int
        val flagC: Boolean
    }
    
    class Options(
        override val optionA: String,
        override val optionB: Int,
        override val flagC: Boolean
    ) : IOptions
    
    external class MyLibrary(options: Options)
    

    用法:

    fun foo() {
        MyLibrary(Options(
            optionA = "foo",
            flagC = true
        ))
    }
    

    需要IOptions 外部接口来防止Options 成员名称被破坏。 Options 类本身不能是外部的,因为 JS 中没有定义这样的类/构造函数。

    需要注意的是,生成的 JS 代码的效率会稍低——将有一个完整的附加类 (Options) 和一个将参数分配给属性的构造函数调用,而不仅仅是一个 JS 对象字面量。但是,一旦涉及的代码被 jit 了,它可能就没有任何区别了。

    【讨论】:

      【解决方案2】:

      但是...有更好的方法吗?有没有办法相处 unsafeCast?

      您可能要考虑继续使用unsafeCast,但将其隐藏。

      external interface Options {
          var optionA: String
          var optionB: Int
          var flagC: Boolean
      }
      
      external class MyLibrary(options: Options)
      
      @OptIn(ExperimentalContracts::class)
      inline fun Options(
          block: Options.() -> Unit
      ): Options {
          
          contract {
              callsInPlace(block, kind = InvocationKind.EXACTLY_ONCE)
          }
          
          return Any().unsafeCast<Options>().apply(block)
      }
      

      用法:

      fun myFunWithApply() {
          MyLibrary(Options {
              optionA = "foo"
              flagC = true
          })
      }
      

      与我的其他答案相比,这应该转化为更高效的 JS 代码。无论如何,这通常并不重要。

      【讨论】:

        猜你喜欢
        • 2023-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多