对于以下javascript API包装器:
@JSName("React")
object React extends js.Object {
def createClass(init: ReactClassInit): ReactClassBuilder = ???
}
建议实例化以下特征的建议是什么
trait ReactClassInit extends js.Object {
val render: js.ThisFunction0[js.Dynamic, js.Any]
}
目前我正在做以下事情:
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。
(这是将所有内容放入更好的上下文的所有代码)
//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"))
}
}
答案 0 :(得分:4)
目前,推荐的方法非常接近您的工作,除了js.Dynamic.literal
的使用应该封装在您的特征的伴随对象中(在您的情况下为ReactClassInit
)。您可以在该伴随对象中提供类型安全的apply
方法,如下所示:
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)
})
当然,这仍然是全球不安全的。但是在代码中只有一点使用强制转换,更重要的是接近类型的定义。因此,如果您更新一个,则更有可能更新另一个。
我知道这不是一个完全令人满意的解决方案。但到目前为止,在我们的Scala.js设计中,我们尚未找到解决此问题的非常好的解决方案。
两个旁注:
1)我强烈建议使用new js.ThisFunctionN { def apply }
!这种符号完全奏效是一个意外。只需使用我在我的例子中展示的lambda。如果目标类型已经被输入为js.ThisFunctionN
(就像在我的代码中那样),它就会像那样工作。如果您的代码中的目标类型为js.Any
(或Any
),则需要将您的lambda归为: js.ThisFunction
(无数字),以确保编译器将其视为此函数而不是(非此函数),但这就是全部。为了更清楚,这里是你的代码看起来如何:
val init = *(render = { (thisArg: js.Dynamic) =>
React.DOM.div(null, "Hello ", thisArg.props.name)
}: js.ThisFunction).asInstanceOf[ReactClassInit]
2)您可能希望将函数输入为Any
(或_
)而不是js.Any
:
trait ReactClassInit extends js.Object {
val render: js.ThisFunction0[js.Dynamic, Any]
}
通常,当您在结果类型js.Any
中使用js.(This)Function
时,您的意思是任何值,而不是任何JS值。 Scala的类型推断最适合该位置的Any
。