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 type | JavaScript type | Restrictions |
---|---|---|
java.lang.String | string | |
scala.Boolean | boolean | |
scala.Char | opaque | |
scala.Byte | number | integer, range (-128, 127) |
scala.Short | number | integer, range (-32768, 32767) |
scala.Int | number | integer, range (-2147483648, 2147483647) |
scala.Long | opaque | |
scala.Float | number | |
scala.Double | number | |
scala.Unit | undefined | |
scala.Null | null | |
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:
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]
js.Dictionary[T]
<–> mutable.Map[String, T]
js.UndefOr[T]
<–> Option[T]
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:
defines a JavaScript function
object which squares its argument.
This corresponds to the following JavaScript code:
You can call a js.FunctionN
in Scala.js with the usual syntax:
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:
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:
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:
will map to
JS functions with varargs
In order to capture varargs from a JS function, create your own trait that
extends js.Function
or js.ThisFunction
.
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.
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
can be written in Scala.js either as
or as
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:
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.
Alternatively, you can use anonymous classes extending js.Object
or a
Scala.js-defined JS trait.