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]
}
}
which you can then use with:
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.
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.
Two side notes:
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) 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]
}
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.