JavaScript types

Understanding how different types are mapped between Scala.js and JavaScript is crucial for correct interoperability. Some types map quite directly (like String) where others require some conversions.

Type Correspondence

Some Scala types are directly mapped to corresponding underlying JavaScript types. These correspondences can be used when calling Scala.js code from JavaScript and when defining typed interfaces for JavaScript code.

Scala typeJavaScript typeRestrictions
java.lang.Stringstring
scala.Booleanboolean
scala.Charopaque
scala.Bytenumberinteger, range (-128, 127)
scala.Shortnumberinteger, range (-32768, 32767)
scala.Intnumberinteger, range (-2147483648, 2147483647)
scala.Longopaque
scala.Floatnumber
scala.Doublenumber
scala.Unitundefined
scala.Nullnull
subtypes of `js.Any` themselves see the facade types guide
other Scala classes
including value classes
opaque, except for exported methods
Note: toString() is always exported
see exporting Scala.js APIs to JavaScript

On the other hand, some JavaScript (collection) types have similar types in Scala. Instead of mapping them directly, Scala.js provides conversions between them. We show with a couple of snippets how you can convert from JavaScript to Scala types and back. Please refer to the Scaladocs for details.

js.FunctionN <–> scala.FunctionN

Functions from JavaScript and Scala are not exactly the same thing, therefore they have different types. However, implicit conversions are available by default to go from one to the other, which means the following snippets compile out of the box:

import scala.scalajs.js

val scalaFun: Int => Int = (x: Int) => x * x
val jsFun: js.Function1[Int, Int] = scalaFun
val scalaFunAgain: Int => Int = jsFun

Most of the time, you don’t even need to worry about those, except if you write facade types for JavaScript APIs, in which case you have to use the JS function types.

js.Array[T] <–> mutable.Seq[T]

import scala.scalajs.js

val jsArr = js.Array(1, 2, 3)

// Scala style operations on js.Array (returns a js.Array)
val x: js.Array[Int] = jsArr.takeWhile(_ < 3)

// Use a js.Array as a Scala mutable.Seq
val y: mutable.Seq[Int] = jsArr

// toArray (from js.ArrayOps) -- Copy into scala.Array
val z: scala.Array[Int] = jsArr.toArray

import js.JSConverters._

val scSeq = Seq(1, 2, 3)

// Seq to js.Array -- Copy to js.Array
val jsArray: js.Array[Int] = scSeq.toJSArray

js.Dictionary[T] <–> mutable.Map[String, T]

import scala.scalajs.js

val jsDict = js.Dictionary("a" -> 1, "b" -> 2)

// Scala style operations on js.Dictionary (returns mutable.Map)
val x: mutable.Map[String, Int] = jsDict.mapValues(_ * 2)

// Use a js.Dictionary as Scala mutable.Map
val y: mutable.Map[String, Int] = jsDict

import js.JSConverters._

val scMap = Map("a" -> 1, "b" -> 2)

// Map to js.Dictionary -- Copy to js.Dictionary
val jsDictionary: js.Dictionary[Int] = scMap.toJSDictionary

js.UndefOr[T] <–> Option[T]

import scala.scalajs.js

val jsUndefOr: js.UndefOr[Int] = 1

// Convert to scala.Option
val x: Option[Int] = jsUndefOr.toOption

import js.JSConverters._

val opt = Some(1)

// Convert to js.Undefined
val y: js.UndefOr[Int] = opt.orUndefined

Pre-defined JavaScript types

Primitive JavaScript types (number, boolean, string, null and undefined) are represented by their natural equivalent in Scala, as shown above.

For other pre-defined JavaScript types, such as arrays and functions, the package scala.scalajs.js (ScalaDoc) provides dedicated definitions.

The class hierarchy for these standard types is as follows:

js.Any
 +- js.Object
 |   +- js.Date
 |   +- js.RegExp
 |   +- js.Array[A]
 |   +- js.Function
 |   |   +- js.Function0[+R]
 |   |   +- js.Function1[-T1, +R]
 |   |   +- ...
 |   |   +- js.Function22[-T1, ..., -T22, +R]
 |   |   +- js.ThisFunction
 |   |       +- js.ThisFunction0[-T0, +R]
 |   |       +- js.ThisFunction1[-T0, -T1, +R]
 |   |       +- ...
 |   |       +- js.ThisFunction21[-T0, ..., -T21, +R]
 |   +- js.Iterable[+A]
 |   +- js.Iterator[+A]
 |   +- js.Promise[+A]
 |   +- js.Thenable[+A]
 +- js.Dictionary[A]
 +- js.Symbol

Note that most of these types are similar to standard Scala types. For example, js.Array[A] is similar to scala.Array[A], and js.FunctionN is similar to scala.FunctionN. However, they are not completely equivalent, and must not be confused.

With the exception of js.Array[A] and js.Dictionary[A], these types have all the fields and methods available in the JavaScript API. The collection types feature the standard Scala collection API instead, so that they can be used idiomatically in Scala code.

Function types

js.Function and its subtypes

js.FunctionN[T1, ..., TN, R] is, as expected, the type of a JavaScript function taking N parameters of types T1 to TN, and returning a value of type R.

There are implicit conversions from scala.FunctionN to js.FunctionN and back, with the obvious meaning. These conversions are the only way to create a js.FunctionN in Scala.js. For example:

val f: js.Function1[Double, Double] = { (x: Double) => x*x }

defines a JavaScript function object which squares its argument. This corresponds to the following JavaScript code:

var f = function(x) {
  return x*x;
};

You can call a js.FunctionN in Scala.js with the usual syntax:

val y = f(5)

js.ThisFunction and its subtypes

The series of js.ThisFunctionN solve the problem of modeling the this value of JavaScript in Scala. Consider the following call to the each method of a jQuery object:

var lis = jQuery("ol > li");
lis.each(function() {
  jQuery(this).text(jQuery(this).text() + " - transformed")
});

Inside the closure, the value of this is the DOM element currently being enumerated. This usage of this, which is nonsense from a Scala point of view, is standard in JavaScript. this can actually be thought of as an additional parameter to the closure.

In Scala.js, the this keyword always follows the same rules as in Scala, i.e., it binds to the enclosing class, trait or object. It will never bind to the equivalent of the JavaScript this in an anonymous function.

To access the JavaScript this in Scala.js, it can be made explicit using js.ThisFunctionN. A js.ThisFunctionN[T0, T1, ..., TN, R] is the type of a JavaScript function taking a this parameter of type T0, as well as N normal parameters of types T1 to TN, and returning a value of type R. From Scala.js, the this parameter appears as any other parameter: it has a non-keyword name, a type, and is listed first in the parameter list. Hence, a scala.FunctionN is convertible to/from a js.ThisFunction{N-1}.

The previous example would be written as follows in Scala.js:

val lis = jQuery("ol > li")
lis.each({ (li: dom.HTMLElement) =>
  jQuery(li).text(jQuery(li).text() + " - transformed")
}: js.ThisFunction)

Skipping over the irrelevant details, note that the parameter li completely corresponds to the JavaScript this. Note also that we have ascribed the lambda with : js.ThisFunction explicitly to make sure that the right implicit conversion is being used (by default it would convert it to a js.Function1). If you call a statically typed API which expects a js.ThisFunction0, this is not needed.

The mapping between JS this and first parameter of a js.ThisFunction also works in the other direction, i.e., if calling the apply method of a js.ThisFunction, the first actual argument is transferred to the called function as its this. For example, the following snippet:

val f: js.ThisFunction1[js.Object, Int, Int] = ???
val o = new js.Object
val x = f(o, 4)

will map to

var f = ...;
var o = new Object();
var x = f.call(o, 4);

JS functions with varargs

In order to capture varargs from a JS function, create your own trait that extends js.Function or js.ThisFunction.

trait JsVarargsFn extends js.Function {
  def apply(args: Any*): Unit
}

val f: JsVarargsFn = { args =>
  println(s"This method was called with ${args.size} args.")
}

Dynamically typed interface: js.Dynamic

Because JavaScript is dynamically typed, it is not often practical, sometimes impossible, to give sensible type definitions for JavaScript APIs.

Scala.js lets you call JavaScript in a dynamically typed fashion if you want to. The basic entry point is js.Dynamic.global, which is a dynamically typed view of the JavaScript global scope. You can select any global variable of JavaScript as a a member of js.Dynamic.global, e.g., js.Dynamic.global.Math, which will be typed as a js.Dynamic.

You can read and write any field of a js.Dynamic, as well as call any method with any number of arguments, and you always receive back a js.Dynamic.

For example, this snippet taken from the Hello World example uses the dynamically typed interface to manipulate the DOM model.

val document = js.Dynamic.global.document
val playground = document.getElementById("playground")

val newP = document.createElement("p")
newP.innerHTML = "Hello world! <i>-- DOM</i>"
playground.appendChild(newP)

In this example, document, playground and newP are all inferred to be of type js.Dynamic.

Literal object construction

Scala.js provides two syntaxes for creating JavaScript objects in a literal way. The following JavaScript object

{foo: 42, bar: "foobar"}

can be written in Scala.js either as

js.Dynamic.literal(foo = 42, bar = "foobar")

or as

js.Dynamic.literal("foo" -> 42, "bar" -> "foobar")

Literal object construction using an Scala object interface

Sometimes for a nicer interface, literal objects can be implemented using a trait interface. The above JavaScript code can be implemented using following code:

trait MyObject extends js.Object {
  val foo: Int = js.native
  val bar: String = js.native
}

A Scala object should be added for typesafe creation, it would help the readability of the code by removing lots of js.Dynamic.literal all over the code.

object MyObject {
  def apply(foo: Int, bar: String): MyObject =
    js.Dynamic.literal(foo = foo, bar = bar).asInstanceOf[MyObject]
}

Alternatively, you can use anonymous classes extending js.Object or a Scala.js-defined JS trait.