在Scala.js中对JavaScript对象的类型安全封装

3

我想写一个关于scala.js的封装,以适用于一个JavaScript库。该库有一个可以这样实例化的对象:

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

看起来很简单。我希望有一个坐标case类和一个围绕着点的包装器。

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

显然,这种方法行不通,因为案例类没有被转换成对象字面量。当然,我可以摆脱Coord案例类,而是将js.Dynamic.literal传递给构造函数,但这不太类型安全。

我还有什么选择?我必须编写一个更高级的包装器,接受Coord并在将其传递给Point对象之前将其转换为对象字面量吗?


可能是重复的问题:https://dev59.com/WF8d5IYBdhLWcg3wmDKf - gzm0
1个回答

4
您有几个选择:

选项对象

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

class Point(coords: Coords) extends js.Object

这是一个针对Coords的类型安全工厂。具体细节请参见此篇SO帖子

点工厂/高级封装

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))
}

将Case类解释为Option对象

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])

伴生对象确实很棒。问题确实是重复的,但很难找到其他的问题。作为替代方案提议(不确定是否可行): 可以将参数标记为“literal”,这样scala.js就可以自动转换case class了。class Point(@JSLiteral coors: Coors) extends js.Object - Markus Joschko
@JSExport@JSExportTopLevel 有什么区别?(在参数上使用 @JSExport 将起作用并执行您期望的操作)。 - gzm0
然后我对JSExport的理解不同。对我来说,它看起来像是将(case)类的字段导出以便在JS中使用。我原本以为是另一种方式。注释JS外观构造函数或方法参数以将对象转换为文字,无论对象类型是否被注释。 但是阅读JSExport文档后,我猜想它也应该可以这样工作。在我的特定示例中,它现在还没有起作用。我仍然需要调试问题所在。 - Markus Joschko
好的,调试完毕。这个JS库使用hasOwnProperty来检查传递对象上是否存在属性。对于我通过JSExport公开的case类属性,它返回false。然后该库会抛出一个错误。 - Markus Joschko
确实。由于具有JSExport属性的对象不是自己的属性,而是通过原型链继承的。如果您需要拥有自己属性的对象,则必须使用js.Dynamic.literal - gzm0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接