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 usedwhen 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.Anythemselvessee the facade types guide
other Scala classesincluding value classesopaque, except for exported methodsNote: toString() is always exportedsee 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, thereforethey have different types. However, implicit conversions are available bydefault to go from one to the other, which means the following snippets compileout of the box:

  1. import scala.scalajs.js
  2. val scalaFun: Int => Int = (x: Int) => x * x
  3. val jsFun: js.Function1[Int, Int] = scalaFun
  4. val scalaFunAgain: Int => Int = jsFun

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

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

  1. import scala.scalajs.js
  2. val jsArr = js.Array(1, 2, 3)
  3. // Scala style operations on js.Array (returns a js.Array)
  4. val x: js.Array[Int] = jsArr.takeWhile(_ < 3)
  5. // Use a js.Array as a Scala mutable.Seq
  6. val y: mutable.Seq[Int] = jsArr
  7. // toArray (from js.ArrayOps) -- Copy into scala.Array
  8. val z: scala.Array[Int] = jsArr.toArray
  9. import js.JSConverters._
  10. val scSeq = Seq(1, 2, 3)
  11. // Seq to js.Array -- Copy to js.Array
  12. val jsArray: js.Array[Int] = scSeq.toJSArray

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

  1. import scala.scalajs.js
  2. val jsDict = js.Dictionary("a" -> 1, "b" -> 2)
  3. // Scala style operations on js.Dictionary (returns mutable.Map)
  4. val x: mutable.Map[String, Int] = jsDict.mapValues(_ * 2)
  5. // Use a js.Dictionary as Scala mutable.Map
  6. val y: mutable.Map[String, Int] = jsDict
  7. import js.JSConverters._
  8. val scMap = Map("a" -> 1, "b" -> 2)
  9. // Map to js.Dictionary -- Copy to js.Dictionary
  10. val jsDictionary: js.Dictionary[Int] = scMap.toJSDictionary

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

  1. import scala.scalajs.js
  2. val jsUndefOr: js.UndefOr[Int] = 1
  3. // Convert to scala.Option
  4. val x: Option[Int] = jsUndefOr.toOption
  5. import js.JSConverters._
  6. val opt = Some(1)
  7. // Convert to js.Undefined
  8. val y: js.UndefOr[Int] = opt.orUndefined

Pre-defined JavaScript types

Primitive JavaScript types (number, boolean, string, null andundefined) are represented by their natural equivalent in Scala, as shownabove.

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:

  1. js.Any
  2. +- js.Object
  3. | +- js.Date
  4. | +- js.RegExp
  5. | +- js.Array[A]
  6. | +- js.Function
  7. | | +- js.Function0[+R]
  8. | | +- js.Function1[-T1, +R]
  9. | | +- ...
  10. | | +- js.Function22[-T1, ..., -T22, +R]
  11. | | +- js.ThisFunction
  12. | | +- js.ThisFunction0[-T0, +R]
  13. | | +- js.ThisFunction1[-T0, -T1, +R]
  14. | | +- ...
  15. | | +- js.ThisFunction21[-T0, ..., -T21, +R]
  16. | +- js.Iterable[+A]
  17. | +- js.Iterator[+A]
  18. | +- js.Promise[+A]
  19. | +- js.Thenable[+A]
  20. +- js.Dictionary[A]
  21. +- 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 toscala.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 haveall the fields and methods available in the JavaScript API.The collection types feature the standard Scala collection API instead, so thatthey 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 JavaScriptfunction taking N parameters of types T1 to TN, and returning a value oftype R.

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

  1. 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:

  1. var f = function(x) {
  2. return x*x;
  3. };

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

  1. val y = f(5)

js.ThisFunction and its subtypes

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

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

Inside the closure, the value of this is the DOM element currently beingenumerated. 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 additionalparameter 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 tothe equivalent of the JavaScript this in an anonymous function.

To access the JavaScript this in Scala.js, it can be made explicit usingjs.ThisFunctionN. A js.ThisFunctionN[T0, T1, …, TN, R] is the type of aJavaScript function taking a this parameter of type T0, as well as Nnormal 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 anon-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:

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

Skipping over the irrelevant details, note that the parameter li completelycorresponds to the JavaScript this. Note also that we have ascribed thelambda with : js.ThisFunction explicitly to make sure that the right implicitconversion 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 isnot needed.

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

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

will map to

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

Dynamically typed interface: js.Dynamic

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

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

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

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

  1. val document = js.Dynamic.global.document
  2. val playground = document.getElementById("playground")
  3. val newP = document.createElement("p")
  4. newP.innerHTML = "Hello world! <i>-- DOM</i>"
  5. playground.appendChild(newP)

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

Literal object construction

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

  1. {foo: 42, bar: "foobar"}

can be written in Scala.js either as

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

or as

  1. 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 usinga trait interface.The above JavaScript code can be implemented using following code:

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

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

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

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