Type checking

Type checking can occur at either compile time or run time. Statically typed languages, such as C++ and Java, do type checking at compile time. Dynamically typed languages, such as Smalltalk and Python, handle type checking at run time. As a dynamically typed language, ActionScript 3.0 has run-time type checking, but also supports compile-time type checking with a special compiler mode called strict mode . In strict mode, type checking occurs at both compile time and run time, but in standard mode, type checking occurs only at run time.

Dynamically typed languages offer tremendous flexibility when you structure your code, but at the cost of allowing type errors to manifest at run time. Statically typed languages report type errors at compile time, but at the cost of requiring that type information be known at compile time.

Compile-time type checking

Compile-time type checking is often favored in larger projects because as the size of a project grows, data type flexibility usually becomes less important than catching type errors as early as possible. This is why, by default, the ActionScript compiler in Adobe Flash CS4 Professional and Adobe Flex Builder is set to run in strict mode.

In order to provide compile-time type checking, the compiler needs to know the data type information for the variables or expressions in your code. To explicitly declare a data type for a variable, add the colon operator ( : ) followed by the data type as a suffix to the variable name. To associate a data type with a parameter, use the colon operator followed by the data type. For example, the following code adds data type information to the xParam parameter, and declares a variable myParam with an explicit data type:

function runtimeTest(xParam:String) 
{ 
    trace(xParam); 
} 
var myParam:String = "hello"; 
runtimeTest(myParam);

In strict mode, the ActionScript compiler reports type mismatches as compiler errors. For example, the following code declares a function parameter xParam , of type Object, but later attempts to assign values of type String and Number to that parameter. This produces a compiler error in strict mode.

function dynamicTest(xParam:Object) 
{ 
    if (xParam is String) 
    { 
        var myStr:String = xParam; // compiler error in strict mode 
        trace("String: " + myStr); 
    } 
    else if (xParam is Number) 
    { 
        var myNum:Number = xParam; // compiler error in strict mode 
        trace("Number: " + myNum); 
    } 
}

Even in strict mode, however, you can selectively opt of out compile-time type checking by leaving the right side of an assignment statement untyped. You can mark a variable or expression as untyped by either omitting a type annotation, or using the special asterisk ( * ) type annotation. For example, if the xParam parameter in the previous example is modified so that it no longer has a type annotation, the code will compile in strict mode:

function dynamicTest(xParam) 
{ 
    if (xParam is String) 
    { 
        var myStr:String = xParam; 
        trace("String: " + myStr); 
    } 
    else if (xParam is Number) 
    { 
        var myNum:Number = xParam; 
        trace("Number: " + myNum); 
    } 
} 
dynamicTest(100) 
dynamicTest("one hundred");

Run-time type checking

Run-time type checking occurs in ActionScript 3.0 whether you compile in strict mode or standard mode. Consider a situation in which the value 3 is passed as an argument to a function that expects an array. In strict mode, the compiler will generate an error, because the value 3 is not compatible with the data type Array. If you disable strict mode, and run in standard mode, the compiler does not complain about the type mismatch, but run-time type checking by Flash Player and Adobe AIR results in a run-time error.

The following example shows a function named typeTest() that expects an Array argument but is passed a value of 3. This causes a run-time error in standard mode, because the value 3 is not a member of the parameter’s declared data type (Array).

function typeTest(xParam:Array) 
{ 
    trace(xParam); 
} 
var myNum:Number = 3; 
typeTest(myNum);  
// run-time error in ActionScript 3.0 standard mode

There may also be situations where you get a run-time type error even when you are operating in strict mode. This is possible if you use strict mode, but opt out of compile-time type checking by using an untyped variable. When you use an untyped variable, you are not eliminating type checking but rather deferring it until run time. For example, if the myNum variable in the previous example does not have a declared data type, the compiler cannot detect the type mismatch, but Flash Player and Adobe AIR will generate a run-time error because it compares the run-time value of myNum , which is set to 3 as a result of the assignment statement, with the type of xParam , which is set to the Array data type.

function typeTest(xParam:Array) 
{ 
    trace(xParam); 
} 
var myNum = 3; 
typeTest(myNum);  
// run-time error in ActionScript 3.0

Run-time type checking also allows more flexible use of inheritance than does compile-time checking. By deferring type checking to run time, standard mode allows you to reference properties of a subclass even if you upcast . An upcast occurs when you use a base class to declare the type of a class instance but use a subclass to instantiate it. For example, you can create a class named ClassBase that can be extended (classes with the final attribute cannot be extended):

class ClassBase 
{ 
}

You can subsequently create a subclass of ClassBase named ClassExtender, which has one property named someString , as follows:

class ClassExtender extends ClassBase 
{ 
    var someString:String; 
}

Using both classes, you can create a class instance that is declared using the ClassBase data type, but instantiated using the ClassExtender constructor. An upcast is considered a safe operation, because the base class does not contain any properties or methods that are not in the subclass.

var myClass:ClassBase = new ClassExtender();

A subclass, however, does contain properties or methods that its base class does not. For example, the ClassExtender class contains the someString property, which does not exist in the ClassBase class. In ActionScript 3.0 standard mode, you can reference this property using the myClass instance without generating a compile-time error, as shown in the following example:

var myClass:ClassBase = new ClassExtender(); 
myClass.someString = "hello"; 
// no error in ActionScript 3.0 standard mode

The is operator

The is operator, which is new for ActionScript 3.0, allows you to test whether a variable or expression is a member of a given data type. In previous versions of ActionScript, the instanceof operator provided this functionality, but in ActionScript 3.0 the instanceof operator should not be used to test for data type membership. The is operator should be used instead of the instanceof operator for manual type checking, because the expression x instanceof y merely checks the prototype chain of x for the existence of y (and in ActionScript 3.0, the prototype chain does not provide a complete picture of the inheritance hierarchy).

The is operator examines the proper inheritance hierarchy and can be used to check not only whether an object is an instance of a particular class, but also whether an object is an instance of a class that implements a particular interface. The following example creates an instance of the Sprite class named mySprite and uses the is operator to test whether mySprite is an instance of the Sprite and DisplayObject classes, and whether it implements the IEventDispatcher interface:

var mySprite:Sprite = new Sprite(); 
trace(mySprite is Sprite); // true 
trace(mySprite is DisplayObject);// true 
trace(mySprite is IEventDispatcher); // true

The is operator checks the inheritance hierarchy and properly reports that mySprite is compatible with the Sprite and DisplayObject classes (the Sprite class is a subclass of the DisplayObject class). The is operator also checks whether mySprite inherits from any classes that implement the IEventDispatcher interface. Because the Sprite class inherits from the EventDispatcher class, which implements the IEventDispatcher interface, the is operator correctly reports that mySprite implements the same interface.

The following example shows the same tests from the previous example, but with instanceof instead of the is operator. The instanceof operator correctly identifies that mySprite is an instance of Sprite or DisplayObject, but it returns false when used to test whether mySprite implements the IEventDispatcher interface.

trace(mySprite instanceof Sprite); // true 
trace(mySprite instanceof DisplayObject);// true 
trace(mySprite instanceof IEventDispatcher); // false

The as operator

The as operator, which is new in ActionScript 3.0, also allows you to check whether an expression is a member of a given data type. Unlike the is operator, however, the as operator does not return a Boolean value. Rather, the as operator returns the value of the expression instead of true , and null instead of false . The following example shows the results of using the as operator instead of the is operator in the simple case of checking whether a Sprite instance is a member of the DisplayObject, IEventDispatcher, and Number data types.

var mySprite:Sprite = new Sprite(); 
trace(mySprite as Sprite);                 // [object Sprite] 
trace(mySprite as DisplayObject);                 // [object Sprite] 
trace(mySprite as IEventDispatcher);                 // [object Sprite] 
trace(mySprite as Number);                                       // null

When you use the as operator, the operand on the right must be a data type. An attempt to use an expression other than a data type as the operand on the right will result in an error.