Jean-Philippe Pellet
Jean-Philippe Pellet

Reputation: 59994

In Scala.js, how to best create an object conforming to a trait from a JS façade?

Suppose I have this definition in Scala.js for the return type of a function of some library I'm using:

@native
trait Link extends Object {
  val href: String = native
  val title: String = native
}

What's the best, typesafe way, in Scala code, to define an object literal conforming to this? I thought the use(...).as[...] trick would work:

val link = js.use(new {
  val href = "value1"
  val title = "value2"
}).as[Link]

But this yields the error:

AnyRef{val href: String; val title: String} does not export a getter href: String

Why?

I also tried this, and as expected it fails:

val link = js.use(js.Dynamic.literal(
  href = cap(2),
  title = cap(3)
)).as[Link]

gives

scala.scalajs.js.Object with scala.scalajs.js.Dynamic does not have a getter href: String

Finally, I also tried this:

val link = new Link {
  override val href = "value1"
  override val title = "value2"
}

and got

A Scala.js-defined JS class cannot directly extend a native JS trait

Right now, I'm doing this:

val link = js.Dynamic.literal(
  href = "value1",
  title = "value2"
).asInstanceOf[Link]

This works, but it's not checked by the compiler if I'm not mistaken.

Upvotes: 5

Views: 1101

Answers (1)

sjrd
sjrd

Reputation: 22085

The library should declare Link as @ScalaJSDefined instead of @js.native. That would allow you to do the third variant (the one with new Link { ... }. I suggest you file a bug report for that library.

As a workaround, either do what you're doing with the .asInstanceOf[Link], or, if you want the js.use(x).as[T] safety, use this:

@ScalaJSDefined
trait LinkImpl extends js.Object {
  val href: String
  val title: String
}

val link = js.use(new LinkImpl {
  val href = "value1"
  val title = "value2"
}).as[Link]

Upvotes: 7

Related Questions