March 1999 Draft
JavaScript 2.0
Types
previousupnext

Wednesday, March 24, 1999

Concepts

The words type and class are used interchangeably in this specification. A type represents a possibly infinite set of values. A value can be a member of multiple such sets, so a value can have more than one type. A value may not have an intrinsic most specific type -- one can ask whether the value v is a member of a given type t, but this does not prevent the value v from also being a member of some unrelated type s. For example, null is a member of type Array as well as type Function, but neither Array nor Function is a subtype of the other.

On the other hand, a variable does have a particular type. If one declares a variable x of type Array, then whatever value is held in x is guaranteed to have type Array, and one can assign any value of type Array to x.

Predefined Types

The following types are predefined in JavaScript 2.0:

Type

Set of Values

void undefined
Null null
boolean   true and false
integer Double-precision IEEE floating-point numbers that are mathematical integers, including positive and negative zeroes but excluding infinities and NaN
number Double-precision IEEE floating-point numbers, including positive and negative zeroes and infinities and NaN
character   Single 16-bit unicode characters
string Immutable strings of unicode characters
Function All functions and null
array All arrays
Array All arrays and null
type All types
Type All types and null
object All values except undefined and null
Object All values except undefined
Any All values

By convention, predefined types whose names start with an upper-case letter include the value null, while predefined types whose names start with a lower-case letter do not include null. User-defined type names do not have to follow this convention.

Unlike in JavaScript 1.x, there is no distinction between objects and primitive values. All values can have methods. Some values can be sealed, which disallows addition of ad-hoc properties. User-defined classes can be made to behave like primitives.

The above type names are not reserved words. They are considered to be defined in a scope that encloses a package's global scope, so a package could use these type names as identifiers. However, defining these identifiers for other uses might be confusing because it would shadow the corresponding type names (the types themselves would continue to exist, but they could not be accessed by name).

The names Boolean, Number, and String have been deliberately left unused to enable implementations to use them to emulate the behavior of the JavaScript 1.x Boolean, Number, and String wrapper objects. These are not part of JavaScript 2.0, but an implementation may support them for compatibility.

The name function could not be used to mean "all functions" because it is a reserved word. Use Function^* instead.

Literals

A literal number that has an integral value has type integer; otherwise it has type number. integer is a subtype of number, so every integer value is also a number value. A literal string that has exactly one 16-bit unicode character has type character; otherwise it has type string. character is a subtype of string, so every character value is also a string value.

User-Defined Types

Any class defined using the class declaration is also a type that denotes the set of all of its and its descendants' instances. These include the predefined classes, so Object, Date, etc. are all types. null is an instance of a user-defined class c if it is an instance of any of c's superclasses.

Compound Types

We can use the following operators to construct more complex types. t and u are type expressions in the expressions below.

Type   Values
t | * null and all values of type t
t ^ * All values of type t except null
t | ? undefined and all values of type t
t ^ ? All values of type t except undefined
t | u All values belonging to either type t or type u or both
t & u All values simultaneously belonging to both type t and type u

The language does not syntactically distinguish type expressions from value expressions, so a type expression can also use any other value operators such as !, +, and . (member access). Except for parentheses, most of them are not very useful, though.

Subtyping

We write a b to denote that a is a subtype of b. Subtyping is transitive, so if a b and b c then a c is also true. Subtyping is also reflexive: a a.

The following subtype and type equivalence relations hold. t, u, and v represent arbitrary types.

t t | u t & u t
t | t = t t & t = t
t | u = u | t t & u = u & t
(t | u) | v = t | (u | v) (t & u) & v = t & (u & v)
t | * = t | Null t | ? = t | void
integer number object character string object
boolean object array object
type object
Array = array | Null Type = type | Null
Object = object | Null
t Any

We write v t to indicate that v is a value that is a member of type t. The following subtyping rule holds: if v t and t s, then v s holds as well. Any particular value v is simultaneously a member of many types.

Meaning of Types

Types are generally used to restrict the set of objects that can be held in a variable or passed as a function argument. For example, the declaration

var integer x;

restricts the values that can be held in variable x to be integers.

A type declaration never affects the semantics of reading the variable or accessing one of its members. Thus, as long as expression new MyType() returns a value of type MyType, the following two code snippets are equivalent:

var MyType x = new MyType();
x.foo();
var x = new MyType();
x.foo();

This equivalence always holds, even if these snippets are inside the declaration of class MyType and foo is a private field of that class. As a corollary, adding true type annotations does not change the meaning of a program.

Type Expressions

A type is also a value (whose type is type) and can be used in expressions, assigned to variables, passed to functions, etc. For example, the code

const type Z = integer;
function abs_val(Z i) Z {
  return i<0 ? -i : i;
}

is equivalent to:

function abs_val(integer i) integer {
  return i<0 ? -i : i;
}

As another example, the following method takes a type and returns an instance of that type:

method QueryInterface(type t) t { ... }

Coercions

Coercions can take place in the following situations:

In any of these cases, if v t, then v is passed unchanged. If v t, then an error occurs unless v is undefined, in which case the following coercions are tried, in order:

  1. If Null t, then null is used instead of undefined.
  2. If boolean t, then false is used instead of undefined.
  3. If integer t, then +0.0 is used instead of undefined.
  4. If string t, then "" is used instead of undefined.

If none of the coercions succeeds, an error occurs.

Some types such as machine integers define additional coercions. These are listed along with descriptions of these types.

@ Operator

One can explicitly request a coercion in an expression by using the @ operator. This operator has the same precedence as . and coerces its left operand to the right operand, which must be a type. ... v@t ... can be used in an expression and has the same effect as:

function coerce_to_t(t a) t {return a}   // Declared at the top level

... coerce_to_t(v) ...

assuming that coerce_to_t is an identifier not used anywhere else. The @ operator is useful as a type assertion as in w@Window. It's a postfix operator to simplify cascading expressions:

w@Window.child@Window.pos

is equivalent to:

(((w@Window).child)@Window).pos

Type Casts

A type cast performs more aggressive transformations than a type coercion. To cast a value to a given type, we use the type as a function, passing it the value as an argument:

type(value)

For example, integer(258.1) returns the integer 258, and string(2+2==4) returns the string "true".

Need to specify the semantics of type casts. They are intended to mimic the current ToNumber, ToString, etc. methods.

Discussion

Colon Syntax

Would we rather have the colon syntax for declaring types? Two sample declarations would be:

var x:integer = 7;
function f(a:integer, b:Object):number {...}

A few considerations:

Type Expressions

Do we want to make type expressions have a distinct syntax from value expressions? I have not heard any "pro" arguments. Here are the "con" arguments:


Waldemar Horwat
Last modified Wednesday, March 24, 1999
previousupnext