为 API 包装器实例化 js.Object 的建议方法是什么 [英] What is the suggested way to instantiate a js.Object for API wrappers

查看:35
本文介绍了为 API 包装器实例化 js.Object 的建议方法是什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于以下 javascript API 包装器:

For the following javascript API wrapper:

@JSName("React")
object React extends js.Object {
  def createClass(init: ReactClassInit): ReactClassBuilder = ???
}

建议实例化以下特征是什么

What is the suggested what to instantiate the following trait

trait ReactClassInit extends js.Object {
  val render: js.ThisFunction0[js.Dynamic, js.Any]
}

目前我正在做以下事情:

Currently I am doing the following:

val * = js.Dynamic.literal
val init = *(render = new js.ThisFunction0[js.Dynamic, js.Any] {
  override def apply(thisArg: js.Dynamic): js.Any = {
    React.DOM.div(null, "Hello ", thisArg.props.name)
  }
}).asInstanceOf[ReactClassInit]
val HelloMessage = React.createClass(init)

我不喜欢这种方法的一点是没有类型安全确保 ReactClassInit 正确实例化.

What I don't like about this approach is that there is no type-safety ensuring that ReactClassInit is instantiated properly.

(这是将事情置于更好的上下文中的所有代码)

(Here is all of the code to put things into a better context)

//Work in progress React wrapers
@JSName("React")
object React extends js.Object {
  def createClass(init: ReactClassInit): ReactClassBuilder = ???
  def renderComponent(cls: ReactClassInstance, mountNode: HTMLElement) = ???
  val DOM: ReactDOM = ???
}

trait ReactDOM extends js.Object {
  def div(huh: js.Any, something: js.String, propsOrWhat: js.Any) = ???
}

trait ReactClassInstance extends js.Object

trait ReactClassBuilder extends js.Object {
  def apply(args: js.Object): ReactClassInstance
}

trait ReactClassInit extends js.Object {
  val render: js.ThisFunction0[js.Dynamic, js.Any]
}



@JSExport
object ReactTodo {
  //Some helpers I use.
  val * = js.Dynamic.literal

  @JSExport
  def main() {
    helloJonT()
  }


  //Ideal Typed Example
  def helloJonT() {
    val init = *(render = new js.ThisFunction0[js.Dynamic, js.Any] {
      override def apply(thisArg: js.Dynamic): js.Any = {
        React.DOM.div(null, "Hello ", thisArg.props.name)
      }
    }).asInstanceOf[ReactClassInit]
    val HelloMessage = React.createClass(init)
    React.renderComponent(HelloMessage(*(name = "Jon").asInstanceOf[js.Object]), document.getElementById("content"))
  }

}

推荐答案

目前,推荐的方法和你正在做的很接近,除了使用 js.Dynamic.literal 应该是封装在您的特征的伴随对象中(在您的情况下为 ReactClassInit).您可以在该伴随对象中提供类型安全的 apply 方法,如下所示:

Currently, the recommended approach is very close to what you are doing, except that the use of js.Dynamic.literal should be encapsulated in the companion object of your trait (ReactClassInit in your case). You can provide a type-safe apply method in that companion object like this:

trait ReactClassInit extends js.Object {
  val render: js.ThisFunction0[js.Dynamic, js.Any]
}
object ReactClassInit {
  def apply(render: js.ThisFunction0[js.Dynamic, js.Any]): ReactClassInit = {
    js.Dynamic.literal(
      render = render
    ).asInstanceOf[ReactClassInit]
  }
}

然后你可以使用:

val init = ReactClassInit(render = { (thisArg: js.Dynamic) =>
  React.DOM.div(null, "Hello ", thisArg.props.name)
})

当然,这仍然是全球不安全的.但是在您的代码中只有一处使用了强制转换,更重要的是它接近类型的定义.所以更有可能的是,如果你更新一个,你就会更新另一个.

Of course this is still globally unsafe. But there is only one point in your code where you use a cast, and more importantly it is close to the definition of the type. So it is more likely that if you update one, you will update the other.

我知道这不是一个完全令人满意的解决方案.但是到目前为止,在我们的 Scala.js 设计中,我们还没有找到一个很好的解决方案.

I know this is not a completely satisfactory solution. But so far in our design of Scala.js we have not yet found a really good solution to this problem.

附注:

1) 我强烈建议不要使用 new js.ThisFunctionN { def apply }!这种表示法完全起作用纯属意外.只需使用我在示例中展示的 lambda.如果目标类型已经输入为 js.ThisFunctionN(就像在我的代码中一样),它会像那样工作.如果,就像你的代码一样,目标类型是 js.Any(或 Any),你需要将你的 lambda 归因于 : js.ThisFunction (不带数字)以确保编译器将其视为此函数而不是(非此)函数,仅此而已.为了更清楚,以下是您的代码的外观:

1) I strongly advise against using new js.ThisFunctionN { def apply }! It is an accident that this notation works at all. Simply use a lambda like I showed in my example. If the target type is typed as a js.ThisFunctionN already (like in my code), it'll work just like that. If, as was the case in your code, the target type is js.Any (or Any), you'll need to ascribe your lambda with : js.ThisFunction (without digit) to make sure that the compiler treats it as a this-function and not a (non-this-)function, but that's all. To make it clearer, here is how it would have looked with your code:

val init = *(render = { (thisArg: js.Dynamic) =>
  React.DOM.div(null, "Hello ", thisArg.props.name)
}: js.ThisFunction).asInstanceOf[ReactClassInit]

2) 您可能希望将函数键入为返回 Any(或 _)而不是 js.Any:

2) You probably want your function to be typed as returning Any (or _) instead of js.Any:

trait ReactClassInit extends js.Object {
  val render: js.ThisFunction0[js.Dynamic, Any]
}

通常,当您在 js.(This)Function 的结果类型中使用 js.Any 时,您的意思是任何值,而不是 任何 JS 值.Scala 的类型推断最适用于该位置的 Any.

Typically when you use js.Any in the result type of js.(This)Function, you mean any value, not any JS value. And Scala's type inference works best with Any in that location.

这篇关于为 API 包装器实例化 js.Object 的建议方法是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆