Announcing Scala.js 0.6.5

Aug 31, 2015.

We are thrilled to announce the release of Scala.js 0.6.5!

This is probably the most important release since Scala.js 0.5.0! It brings significant advances to interoperability with JavaScript:

  • You can now implement subclasses of JavaScript classes and traits in Scala.js!
  • Using the same semantics, you can now define object literals with new js.Object { val x = 5 }
  • There is a new unboxed pseudo-union type A | B to more accurately type your JavaScript facades
  • You can statically typecheck that the @JSExports of a Scala class comply with a JavaScript facade trait with js.use(x).as[T]

Getting started

If you are new to Scala.js, head over to the tutorial.

Release notes

For changes in the 0.6.x series compared to 0.5.x, read the announcement of 0.6.0.

As a minor release, 0.6.5 is backward source and binary compatible with previous releases in the 0.6.x series. Libraries compiled with earlier versions can be used with 0.6.5 without change. However, it is not forward compatible: libraries compiled with 0.6.5 cannot be used by projects using 0.6.{0-4}.

Please report any issues on GitHub.

Important new warnings

When upgrading from 0.6.{0-4}, you will get new warnings on the declarations of your facade types, i.e., traits, classes and objects extending js.Any. For example:

import scala.scalajs.js

class Foo extends js.Object {
  def bar(x: Int): Int = js.native
}

will have the following warning:

Foo.scala:3: warning: Classes, traits and objects inheriting from js.Any should be annotated
  with @js.native, unless they have @ScalaJSDefined.
  The default will switch to Scala.js-defined in the next major version of Scala.js.
class Foo extends js.Object
      ^

As the text says, you should simply add the @js.native annotation to the declaration of Foo to silence the warning:

import scala.scalajs.js

@js.native
class Foo extends js.Object {
  def bar(x: Int): Int = js.native
}

Addressing these warnings is important to make your source code forward compatible with the next major version of Scala.js. An unannotated declaration extending js.Any will by default be Scala.js-defined (see next section) in the next version.

Improvements

Scala.js-defined JS classes, objects, and traits

Scala.js 0.6.5 introduces a major language improvement: the ability to define, in Scala.js, a subclass of a native JavaScript class (or implementing a JavaScript trait/interface). We call such classes Scala.js-defined JS classes, because they are effectively JavaScript classes, but written in Scala.js; whereas classes that you typically write are Scala classes, not JavaScript classes.

import scala.scalajs.js
import scala.scalajs.js.annotation._

@ScalaJSDefined
class Foo extends js.Object {
  def bar(x: Int): Int = x + 1
}

Note that the body of bar() is implemented in Scala.js, instead of being = js.native.

A Scala.js-defined JS class is not a facade type to a JavaScript library. Instead, it is fully implemented in Scala.js. Unlike Scala classes, which require exports, all members of Scala.js-defined JS classes are automatically visible from JavaScript code. The class itself is not automatically visible; if you want it to be, you can @JSExport it. You can also use js.constructorOf[C] to obtain the JS constructor function and pass it to a JavaScript library.

Scala.js-defined JS classes have JavaScript semantics instead of Scala semantics. You can read more about that in the documentation. Most importantly, that means overloading dispatch is done at run-time instead of compile-time.

You can also declare Scala.js-defined JS objects as singletons, just like objects in Scala.

Traits

Scala.js-defined JS traits are restricted: they cannot declare any concrete term member, i.e., all their vals, vars and defs must be abstract.

import scala.scalajs.js
import scala.scalajs.js.annotation._

@ScalaJSDefined
trait Foo extends js.Object {
  val x: Int     // ok
  val y: Int = 5 // illegal

  def foo(x: Int): Int         // ok
  def bar(x: Int): Int = x + 1 // illegal
}

Scala.js-defined JS classes, objects and traits cannot directly extend native JS traits (i.e., non-Scala.js-defined JS traits).

Anonymous classes and object literals

Anonymous classes extending a JS class and/or trait are automatically Scala.js-defined. Combined with Scala.js-defined JS traits, this is very useful to write typechecked object literals with Scala syntax:

import scala.scalajs.js
import scala.scalajs.js.annotation._

@ScalaJSDefined
trait Position extends js.Object {
  val x: Int
  val y: Int
}

val obj = new Position {
  val x = 5
  val y = 10
}

In previous versions, obj would have been written with a non-typechecked js.Dynamic.literal:

import scala.scalajs.js

val obj = js.Dynamic.literal(
    x = 5,
    y = 10
).asInstanceOf[Position]

Pseudo-union type A | B

Many JavaScript libraries have APIs with parameters or values that accept different types of values. To be able to accurately type those libraries, Scala.js 0.6.5 features an unboxed, facade-friendly pseudo-union type A | B. Here are a couple of examples of what it can do:

import scala.scalajs.js.|

val a: Int | String = 5
val b: Int | String = "hello"
val c: String | Int = a
val d: Int | Boolean | String = true
val e: Int | Boolean | String = c
val f: AnyVal | String = e
val g: Any = f.merge

// the following examples do not compile
val x: Int | String = 3.4
val y: Int | Boolean = d

See the complete test cases to get the complete picture.

js.use(x).as[T]: statically typecheck your exports

Sometimes, you @JSExport members of your Scala classes so that they comply with some JavaScript interface, for example to pass it to a JavaScript library expecting some fields and methods on your object. In 0.6.4 and before, you needed to take care yourself of exporting everything that was required, and then probably do a hard-cast:

import scala.scalajs.js

trait SomeInterface extends js.Object {
  val x: Int = js.native
  def foo(x: Int): Int = js.native
}

object SomeLibrary extends js.Object {
  def doSomething(obj: SomeInterface): Unit = js.native
}

class InterfaceImpl {
  @JSExport val x: Int = 4
  @JSExport def foo(x: Int): Int = x + 1
}

SomeLibrary.doSomething(new InterfaceImpl().asInstanceOf[SomeInterface])

If you mess up your exports, you will have trouble at run-time.

In 0.6.5, you can write the following instead:

SomeLibrary.doSomething(js.use(new InterfaceImpl()).as[SomeInterface])

Unlike x.asInstanceOf[T], the js.use(x).as[T] idiom statically typechecks that you have all the exports required to comply to the JavaScript interface.

Java library additions

  • The complete set of Character.isXYZ methods (to test Unicode properties of characters)
  • java.lang.Math.rint(Double)
  • java.util.concurrent.ThreadLocalRandom
  • java.util.TreeSet and java.util.NavigableSet

Bug fixes

Among others, the following bugs have been fixed:

  • #1818 Performance bottleneck in one of the steps of the linker (fast- and fullOptJS)
  • #1759 new Int8Array(n).toArray throws TypeError (second run)
  • #1790 Compiler crash with a dash in a parameter of a lambda
  • #1799 java.lang.Iterable is incorrectly in java.util
  • #1819 Double doesn’t match Float even with non-strict floats
  • #1836 BigInteger.ONE.gcd(x) loops forever
  • #1857 j.l.Math.{abs,min,max} do not handle correctly -0.0
  • #1777 Bug with java.util.LinkedList.size when larger than Int.MaxValue.

You can find the full list on GitHub.