我正在尝试按照Paths.js为我的库the official guide编写一个类型化的外观。
我希望能够翻译的内容如下:
var Polygon = require('paths-js/Polygon');
var polygon = Polygon({
points: [[1, 3], [2, 5], [3, 4], [2, 0]],
closed: true
});
到
val polygon = Polygon(points = List((1, 3), (2, 5), (5, 6)), closed = true)
但我不确定到达这一点需要做些什么。
我所做的是以下内容
type Point = (Number, Number)
trait PolygonOpt {
val points: Array[Point]
val closed: Boolean
}
@JSName("paths.Polygon")
object Polygon extends js.Object {
def apply(options: PolygonOpt): Shape = js.native
}
然后,我可以像
一样调用它class Opt extends PolygonOpt {
val points: Array[Point] = Array((1, 2), (3, 4), (5, 6))
val closed = true
}
val opts = new Opt
val poly = Polygon(opts)
我对此有些疑惑:
PolygonOpt
的实例,而运行时需要一个javascript对象文字Point
的定义,翻译成带有两个组件的js数组?Polygon.apply
一样重载def apply(points: Seq[Point], closed: Boolean): Shape
,但scala.js不允许我在Polygon
内编写方法实现,因为它扩展了js.Object
此外,我有两个版本的库使用common.js(分为几个文件,每个组件一个),另一个可以用作单个<script>
标签,将所有内容置于其中命名空间paths
(我现在正在使用它)。
哪一个更适合Scala.js包装?
答案 0 :(得分:4)
首先,请务必阅读JS interop doc,包括calling JavaScript guide。我想你已经这样做了,因为你已经有了一些合理的东西。但是,除非明确提到,否则您应特别注意那些表示Scala类型和JavaScript类型完全不相关的部分。
因此,Int
是一个合适的JS number
(在int的范围内)。但是Array[Point]
与JavaScript数组无关。 Tuple2
(例如(1, 3)
)甚至更少。所以:
Point
的定义是否已翻译成带有两个组件的js数组?
不,不是。因此,JavaScript完全不可理解。它是opaque。
更糟糕的是,PolygonOpt
,因为它不会延伸js.Object
,因此JavaScript也完全不透明,这解释了为什么您看不到字段points
和closed
要做的第一件事就是使用JavaScript可理解的类型(扩展js.Object
)准确输入您的JS API。在这种情况下,它看起来像这样:
type JSPoint = js.Array[Int] // or Double
trait PolygonOpts extends js.Object {
val points: js.Array[JSPoint] = js.native
val closed: Boolean = js.native
}
@JSName("paths.Polygon")
object Polygon extends js.Object {
def apply(options: PolygonOpt): Shape = js.native
}
现在,问题是创建 PolygonOpts
的实例并不容易。有关此内容,请参阅this SO question:
object PolygonOpts {
def apply(points: js.Array[JSPoint], closed: Boolean): PolygonOpts = {
js.Dynamic.literal(
points = points,
closed = closed
).asInstanceOf[PolygonOpts]
}
}
最后,您可以首先使用隐式扩展来公开您想要的Scala-esque API:
import js.JSConverters._
object PolygonImplicits {
implicit class PolygonObjOps(val self: Polygon.type) extends AnyVal {
def apply(points: List[(Int, Int)], closed: Boolean): Shape = {
val jsPoints =
for ((x, y) <- points.toJSArray)
yield js.Array(x, y)
Polygon(PolygonOpts(jsPoints, closed))
}
}
}
隐式扩展是编写扩展js.Object
的对象时可用的Scala方法的方法,因为正如您所发现的那样,您实际上无法实现 in 中的方法{{1} }。