在 KotlinJS 上实例化需要“new”关键字的 Javascript 类

Jac*_*Jac 5 javascript kotlin kotlin-js-interop kotlin-js

考虑以下 javascript 代码(部分取自 Apollo Server 文档),它创建 ApolloServer 的实例并启动它。


const {ApolloServer} = require('apollo-server')

const server = new ApolloServer({ ... });

server.listen().then(({ url }) => {
  console.log(`Server ready at ${url}`);
});

Run Code Online (Sandbox Code Playgroud)

现在考虑使用 KotlinJS 复制相同的行为。首先,Kotlin 没有“new”关键字并且ApolloServer()按预期调用,不会工作但会引发错误(类型错误:类构造函数 ApolloServer 不能在没有“new”的情况下被调用)。

// We can banally represent part of the code above like:
external fun require(module: String): dynamic
val ApolloServer = require("apollo-server").ApolloServer

// ApolloServer is a js class

Run Code Online (Sandbox Code Playgroud)

声明一个外部类,如:

external open class ApolloServer() {
    open fun listen(vararg opts: Any): Promise<Any>
    operator fun invoke(): Any
}
Run Code Online (Sandbox Code Playgroud)

并将其设置为 ApolloServer 类型没有帮助。

我们如何复制“new ApolloServer()”调用?

Jac*_*Jac 4

为了解决这个问题,我发现了一种基于 JsModule 注释的有趣方法。我们需要创建一个 Kotlin 文件来表示我们想要导入的 javascript 模块,在我的例子中是“apollo-server”。

@file:JsModule("apollo-server")
@file:JsNonModule
package com.package

import kotlin.js.Promise

external interface ServerInfo {
    var address: String
    var family: String
    var url: String
    var subscriptionsUrl: String
    var port: dynamic /* Number | String */
        get() = definedExternally
        set(value) = definedExternally
    var subscriptionsPath: String
    var server: Any
}

external open class ApolloServer(config: Any? /* ApolloServerExpressConfig & `T$0` */) : Any {
    open var httpServer: Any
    open var cors: Any
    open var onHealthCheck: Any
    open var createServerInfo: Any
    open fun applyMiddleware()
    open fun listen(vararg opts: Any): Promise<ServerInfo>
    open fun stop(): Promise<Unit>
}
Run Code Online (Sandbox Code Playgroud)

通过上面的代码,我们基本上描述了我们期望在 apollo-server 模块中找到什么以及如何将其映射到 Kotlin 中。

在我们的 Kotlin main 函数中,我们不必指定任何 require(...) ,而只需使用我们的 ApolloServer 类,例如:

    ApolloServer(null).listen().then {
       console.log(it)
    }
Run Code Online (Sandbox Code Playgroud)

使用这种方法,Kotlin 将使用 javascript 中的 new 关键字正确地转译它。

转译版本摘录:

  function main$lambda(it) {
    console.log(it);
    return Unit;
  }
  function main() {
    (new ApolloServer(null)).listen().then(main$lambda);
  }
Run Code Online (Sandbox Code Playgroud)

这段代码只是一个例子,如果没有正确的配置,ApolloServer 将不会被初始化,例如,本例包含一个可为空的配置。