5

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.

Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234

1 Answers1

7

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]
sjrd
  • 21,805
  • 2
  • 61
  • 91
  • 1
    Thank you. I'm the one who wrongly wrote the façade types. So what's the advantage of ever defining `@js.native` traits instead of `@ScalaJSDefined` traits in this case? – Jean-Philippe Pellet Jul 19 '17 at 14:52
  • 1
    In this case, none. For traits, only define them as `@js.native` if you have no other choice (e.g., because it contains an `apply` method). – sjrd Jul 19 '17 at 19:33
  • @sjrd `ScalaJSDefined` is now gone I believe and no annotation is needed to be able to instantiate a `trait` correct? – ecoe Apr 27 '22 at 21:04
  • That is correct. No annotation is the new `@ScalaJSDefined`. – sjrd Apr 27 '22 at 22:27