3

I want to write a scala.js wrapper around a javascript library which has an object that can be instantiated like that:

new Point({x: 10, y: 12})

Seems to be straightforward. I would like to have a coordinate case class and a wrapper around the point.

case class Coord(x: Int, y: Int)
class Point(coord: Coord) extends js.Object

That obviously doesn't work as the case class is not translated into an object literal. I could of course get rid of the Coord case class and instead pass a js.Dynamic.literal to the constructor but that is not very typesafe.

What other option do I have? Do I have to write a higher level wrapper that accepts the Coord and transforms it to an object literal before passing it to the Point object?

  • Possible duplicate of: http://stackoverflow.com/questions/26638171/how-do-i-create-options-objects-in-scala-js – gzm0 May 29 '15 at 20:55

1 Answers1

4

You have a couple of options:

Option Object

trait Coords extends js.Object {
  def x: Int = js.native
  def y: Int = js.native
}

class Point(coords: Coords) extends js.Object

and a typesafe factory for Coords. Details in this SO post

Point Factory / Higher Level Wrapper

case class Coords(x: Int, y: Int)

object Point {
  def apply(coords: Coords): Point = new Point(
    js.Dynamic.literal(x = coords.x, y = coords.y))
}

Interpret Case Class as Option Object

trait PointCoords extends js.Object {
  def x: Int = js.native
  def y: Int = js.native
}

@JSExportAll
case class Coords(x: Int, y: Int)

val c = Coords(1, 2)
new Point(js.use(c).as[PointCoords])
Community
  • 1
  • 1
gzm0
  • 14,752
  • 1
  • 36
  • 64
  • The companion object is really great. The question is indeed a duplicate but it is hard to find the other questions. As an alternative solution proposal (not sure if this is at all feasible): Couldn't a parameter be marked as "literal" so that scala.js can convert the case class automatically? class Point(@JSLiteral coors: Coors) extends js.Object – Markus Joschko May 30 '15 at 18:04
  • What is the difference between that an `@JSExport`? (`@JSExport` on a parameter will work and do what you expect). – gzm0 May 30 '15 at 19:47
  • Then I understand JSExport differently. For me it looks like JSExport is exporting fields of a (case) class to be available in JS. I thought the other way round. Annotating the JS facades constructor or method parameters to transform objects into literals, no matter if the object type is annotated. But reading the JSExport docu I guess it should work that way as well. In my particular example it is not working right now. I still need to debug what the problem is. – Markus Joschko May 31 '15 at 20:51
  • OK, did the debugging. The JS library is using hasOwnProperty to check whether a property exists on the passed object. That returns false for the property that I have exposed via JSExport on the case class. The library then throws an error. – Markus Joschko May 31 '15 at 21:08
  • Indeed. Since the object with the `JSExport`ed property does not have it as *own* property. It is inherited through the prototype chain. If you need an object that has its own property, you must use `js.Dynamic.literal`. – gzm0 May 31 '15 at 21:16