TypeScript for ActionScript Developers

TypeScript for ActionScript Developers


Introduction

The following covers what the TypeScript language is compared to ActionScript 1, 2, and 3 with as much context as possible and compares the syntax side by side. This article should help serve as a reference while you learn TypeScript. Also, these articles always help me learn while writing them and I like to have documentation I can refer to later.

You cannot really talk about ActionScript without talking about the Flash Player internals just like you can’t really talk about JavaScript for web development without also talking about browser internals. Thus, I’ve tried my best to make what the ActionScript equivalents are in the JavaScript browser world.

What is TypeScript?

TypeScript is awesome. It’s this new language from Microsoft that enhances JavaScript. Like Robert Penner said, it’s basically ActionScript 2. You get strong-typing at compile time, but your code is compiled to ActionScript 1/JavaScript at runtime and has no enforcement of types. It has a variety of features, but the only two that really matters are the optional strong-typing and the ability to work with existing JavaScript libraries in a strongly-typed way. There are more, but the only ones ActionScript developers will really care about are:

  1. Optional Typing
  2. Classes
  3. Packages via Namespaces
  4. supports private
  5. modules
  6. everything compiles to JavaScript
  7. everything using classes/packages/modules compiles to RequireJS/AMD/CommonJS
  8. You can code in normal JavaScript; TypeScript is optional; you can mix and match
  9. all the TypeScript features for typing aren’t compiled into JavaScript (no enforcement) so it’s normal JavaScript that gets outputted

Again, there are some other cool features such as overloading, interfaces, statics, follows ECMAScript 6, allows you to “use” ECMAScript 6 now, open source, blah blah blah, etc. I’m only focusing on that ones that matter.

Here its creator, Anders Hejlsberg of Turbo Pascal, Delphi, and C# fame, does a 1 hour overview.

Why TypeScript?

If you want strong-typing in JavaScript, you’ll love TypeScript. Its typing is near identical to ActionScript 2 and 3.

If you are a server-side developer who prefers to render the client side, think Google Closure annotations are sufficient, unit tests who’s sole purpose is to catch spelling errors & type casting errors, or believe “loose typing” is a great feature of JavaScript, peace the eff out.

To quote Cliff Hall on a recent post defending languages that compile to JavaScript on Google+:

They let you apply useful abstractions like classes, interfaces, inheritance, encapsulation, and OOP design patterns. This allows you to build larger systems in a more maintainable way, that is more team-friendly.

Yep. For me, spelling mistakes and type issues that aren’t parsing related should be trivial issues, but instead have a major detrimental impact because of the current JavaScript development landscape and browser runtime issues (swallowed exceptions, misleading error messages, still not developer friendly compared to more mature runtimes, etc). TypeScript, at least in Visual Studio, will help alleviate the spelling & type casting issues, and if you do write unit tests, they’ll test more important things.

How? TypeScript Compiler

TypeScript the compiler is written in TypeScript. The compiler will output to JavaScript, optionally with header definitions. If you go to http://www.typescriptlang.org/ you can learn how to install and/or download what you need. I used the Node install. For Mac, this is nice because it makes it global so you can play in the Mac Terminal.

npm install -g typescript

If you want to compile some TypeScript to a JavaScript file in the same directory, on a command line, go:

tsc helloworld.ts

You’ll know if it worked if you see a helloworld.js file right next to your .ts file in the same directory. The compiler can also watch a directory/file as well similar to how people will have a Node server running a CoffeeScript watch compiler or like the SASS plugin for Sublime that’ll compile to CSS on save.

An easier way to play with the language is just to go to TypeScript Playground. You can also see errors in real time that you can hover over to see why they are errors.

How does it handle globals/internals?

While ActionScript was ECMAScript 3 and 4 based, it like JavaScript, had globals. In ActionScript running in the Flash Player, it had some definition/declaration files that were compiled into the playerglobal.swc (I forget how this worked in AS1 and AS2). This library used the “native” keyword. Since most of these functions/variables/classes were defined in C for ActionScript at runtime, native provided by the bridge to allow your ActionScript 3 to work with those reserved keywords, globals, etc.

TypeScript provides the same thing via “ambient” declarations. They do this both for the aforementioned reason (window, document, console, etc.) but also for strongly-typed access to existing libraries of your choosing (jQuery, Backbone, etc). Hot as hell, right? I KNOW! They already provide a lib.ds.ts for common web things like document, window, etc so you don’t have to add those yourself.

Here is part of the ActionScript 3 Sprite class with the stopDrag function:

[native(cls="SpriteClass",instance="SpriteObject",methods="auto",customconstruct="true")] public class flash.display.Sprite extends flash.display.DisplayObjectContainer
{
   native public function stopDrag():void;
}

And here is the equivalent in TypeScript:

// You can do shorthand
declare module flash.display
{
   export class Sprite
   {
      public stopDrag():void;
   }
}
 
// or long hand
declare module flash
{
   export module display
      {
         export class Sprite
         {
            public stopDrag():void;
         }
      }
}

 

How does it integrate with JavaScript?

One way is using the ambient declarations above. Another is declaration files. If you’re familiar with C or Objective C, they have these header files. They’re basically like an interface to their corresponding class. They have all the getters/setters, public variables, and methods, but nothing in the contents. It’s just a simple file that describes how you interact with the class. It allows basically runtime machine code to have an interface at runtime.

TypeScript has declaration files as well. If you add –declarations to the tsc compiler, you can see these files with a .d.js extension added. When compiling against JavaScript libraries that aren’t written in TypeScript, you can use these declarations files to get strong-typing with JavaScript. #NOMNOMNOM

File Types

TypeScript files are saved with a .ts extension. They’ll output a .js file equivalent when compiled unless you output all to a single .js file.

Variable Types

In ActionScript 2 and 3, you define a String function like so:

function setName(firstName:String)

In TypeScript, you do it like so:

setName(firstName:string)

Notice how ActionScript 3 is capitalized and TypeScript is lowercase. The same holds true for Number and Boolean.

ActionScript:

function isOldAge(value:Number):Boolean

vs.

TypeScript:

isOldAge(value:number):bool

Yes, many of us have pleaded with Microsoft to change bool to Boolean. Yell at ‘em on the bug base or on Twitter.
If you don’t define a type, in ActionScript it’s considered “*”. In TypeScript, it’s considered “any”. You can use this on purpose to provide a more clear definition of what the type is vs. “I don’t know”.

For example, here is the quick and dirty way to use an ambient to declare jQuery for general use:

declare var $:any;

Like in ActionScript, you shouldn’t use any in TypeScript, unless you honestly don’t know, are under a tight deadline, a hipster using loose typing in a strongly-typed language, or are a slack bastard. I’m sure someone will point out a justified use case in the comments.

Null and Undefined

Since ActionScript 1, for the most part you could use == with confidence to compare a variable to null or undefined.

AS1/2/3:

var cow;
if(cow == null)
{
   trace("cow is null");
}
 
if(cow == undefined)
{
   trace('cow is undefined");
}

You do that in JavaScript, it’ll explode in your face. You have to use the whole typeof crap:

if(typeof cow != 'undefined')

The same rules apply in TypeScript. While the generated code does it’s best to use strict equality in some places, a lot of the mundane code you write, it does not. Just keep in mind the same null vs. undefined vs. exceptions rules still apply. Yes, the strong typing is supposed to help this very symptom, but it’s still technically JavaScript, and right now the compiler doesn’t have a “always use strict equality” option.

Oddly, it doesn’t put “use strict”; in the outputted JavaScript either, but I get why since that goes against the philosophy of TypeScript itself being the help, not its generated output.

Function Type Inference

Note that TypeScript has type inference already. ActionScript 3 is fixin’ to get it via the Falcon compiler. This means for a lot of functions, you do not need to define a return type, nor cast the return storing value.

In ActionScript 3 type inference:

// notice no return type defined
function getConcatenatedName(firstName:String, lastName:String)
{
   return lastName + ", " + firstName;
}
// not this
// var name:String = getConcatenatedName(firstName, lastName);
var name = getConcatenatedName(firstName, lastName);

TypeScript is the same way:

getConcatenatedName(firstName:string, lastName:string)
{
   return lastName + ", " + firstName;
}
var name = getConcatenatedName(firstName, lastName);

This has runtime implications using the new compiler in ActionScipt 3, and zero impact in your outputted JavaScript from TypeScript; it’s just for type checking before you compile.

This ALSO works with… Interfaces, not just custom Classes and primitive types.

Interface Types

A common use case for Interfaces in both Java and ActionScript is to use Interfaces for more flexible strong-typing via Composition compared to Inheritance. While in ActionScript you have to have a class implement it, in TypeScript, a simple Object can implement it. Yes, you can technically do this in ActionScript 3 with dynamic + implements, but that makes no sense, ese.

Additionally, interfaces in TypeScript support optional contracts by ending the variable name with a “?”. In ActionScript, you can basically define this variable on the concrete class + constructor, so while the interface won’t enforce it, you would at least get a code-hint in your IDE. It’s similar to optional SkinParts in Flex 4’s Spark. It’s part of the design contract, but you don’t HAVE to implement that visual part of the component if you don’t want to. Additionally, you can’t use the Object literal syntax, you have to use a class constructor. Neither of which is the same thing, nor as cool as what you can do in TypeScript.

In ActionScript:

interface Friend
{
	public var name:String;
}
 
class MyFriend extends Object implements Friend
{
	public var name:String;
	public var favoriteColor:String;
 
	public function MyFriend(name:String, favoriteColor:*=null)
	{
		this.name = name;
		this.favoriteColor = favoriteColor;
	}
}
 
function add(friend:Friend)
{
	var name = friend.name;
}
add(new MyFriend("Fred"));  // Ok
add(new MyFriend( , "blue"));  // Error, name required as 1st parameter
add(new MyFriend("Jill", "green"));  // Ok

In TypeScript:

interface Friend
{
    name: string;
    favoriteColor?:string;
}
function add(friend: Friend)
{
   var name = friend.name;
}
add({ name: "Fred" });  // Ok
add({ favoriteColor: "blue" });  // Error, name required
add({ name: "Jill", favoriteColor: "green" });  // Ok

Notice those are simple Objects that have to adhere to an interface; hotness!

Function Literals

Unless they’re from a Ruby or Python background, most people do not use function literals in ActionScript 3. However, this is uber-common in JavaScript, and TypeScript supports that style of definition as well.

In ActionScript:

var isPreferredVendor:Function = function(vendor:Vendor):Boolean
{
   if(vendor.score > 0 && Browser.supportedDevice(vendor.deviceType) == true)
   {
      return true;
   }
   else
   {
      return false;
   }
};
vendorArray.filter(isPreferredVendor);

In TypeScript:

var isPreferredVendor = (vendor:Vendor):bool
{
   if(vendor.score > 0 && Browser.supportedDevice(vendor.deviceType) == true)
   {
      return true;
   }
   else
   {
      return false;
   }
};
vendorArray.filter(isPreferredVendor);

Notice that is slightly different with a function literal type, such as defining a callback. In JavaScript, callbacks, especially inline as closures, are used all over the place. Since they are both defined and used in the same place you can define this slightly differently in TypeScript to ensure any callback used follows the correct contract, like so:

TypeScript:

var isPreferredVendor:(vendor:Vendor)=>bool;

Notice the output JavaScript is just:

var isPreferredVendor;

This ensures when you accidentally use this function:

loadFile(fileName:string, callback:(xhr:XHR,data:string,errorCode:number)=>void)

…like this:

loadFile("myTemplate.hbs", function(data:string, errorCode:number));

Thus, you get strong-typing for anonymous function callbacks. YET MOAR SICKNESS.

This is the main selling point to traditional JavaScript developers. Since they use anonymous function callbacks all the time, this helps confirm the function you write follows the required signature. In the case of jQuery and other libraries, TypeScript supports function overloading so you can support multiple forms of callbacks… all with strong-typing enforcement.

Let’s review this section. The first cool thing is you can make plain Object literals follow interfaces. Second, you can enforce anonymous functions also follow an interface. Awesome and a low hanging fruit via strong-typing for a lot of JavaScript development.

Classes

Classes in TypeScript are a lot like they are in AS2. You can get enforcement at compile time, but at runtime, it just compiles to Objects and prototypes. They WILL work with RequireJS and that’s all that really matters.

Behold, the following ActionScript class:

package
{
	class Greeter
	{
		private var greeting:String;
 
		function Greeter(message:String)
		{
			this.greeting = message;
		}
 
		public function greet():void
		{
			return "Hello, " + greeting;
		}
	}
}

Is equivalent to the TypeScript class:

class Greeter
{
	greeting: string;
 
	constructor (message: string)
	{
		this.greeting = message;
	}
 
	greet()
	{
		return "Hello, " + this.greeting;
	}
}

Notice you don’t have to say public as it’s implied to be public unless you use private. You CAN use public if you wish; again, it doesn’t affect outputted syntax (except for constructor’s; we’ll get to that in a bit). Also note, while private variables are not exported in any way special into your outputted class, they ARE initialized in the constructor if you give them default values in your class declaration. You can see in the generated JavaScript that it is smart enough to set it in the constructor with the class default first, and then in my constructor it updates the value below.

var Greeter = (function () {
    function Greeter(message) {
        this.cow = "cheese";
        this.greeting = message;
        this.cow = "sup";
    }
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    return Greeter;
})();

Notice while it doesn’t use prototype for public variables (ie greeting), it does do it for methods so you at least get the runtime optimization that some JavaScript engines offer vs using closures. Also notice you still have to use “this” everywhere, weeeee!

Class Constructor Initializers

It’s a good practice not to do work in constructors. In ActionScript 3, constructor code compiling in mxmlc isn’t JIT’d. However, developers do it anyway, especially in the JavaScript world where tight syntax leads faster code, smaller file sizes, and the unreadable/hard to debug globs called “chaining”.

TypeScript provides s shortcut that helps out a lot. Notice the use of the keyword “public” before the constructor parameters:

module geom
{
	export class Point()
	{
		x:number;
		y:number;
		constructor(public x:number, public y:number)
		{
		};
	}
}

Notice they automatically set the this.x and this.y to whatever you pass in the JavaScript:

var geom;
(function (geom) {
    var Point = (function () {
        function Point(x, y) {
            this.x = x;
            this.y = y;
        }
        return Point;
    })();
    geom.Point = Point;    
})(geom || (geom = {}));

Rad, right?

They also provide default parmeter values if the developer doesn’t pass a value. Notice I changed the constructor to default to 0 in case the developer doesn’t pass anything. While ActionScript would look like this:

package geom
{
   public class Point
   {
      public var x:Number;
      public var y:Number;
 
      public function Point(x:Number=0, y:Number=0)
      {
         this.x = x;
         this.y = y;
      }
   }
}

And TypeScript:

module geom
{
	export class Point()
	{
		x:number;
		y:number;
		constructor(public x:number=0, public y:number=0)
		{
		};
	}
}

The slight difference is that the parameters are not required in ActionScript where in this TypeScript example they are. I’ll explain this difference more in the next section.

Notice they DO use strict equality in this case in the JavaScript:

var geom;
(function (geom) {
    var Point = (function () {
        function Point(x, y) {
            if (typeof x === "undefined") { x = 0; }
            if (typeof y === "undefined") { y = 0; }
            this.x = x;
            this.y = y;
        }
        return Point;
    })();
    geom.Point = Point;    
})(geom || (geom = {}));

Moooarrr on constructoooorrrrrr

While constructor keyword allows you to define parameters:

constructor(x:number, y:number)

And optional parameters.

ActionScript:

public function Point(x:Number=0, y:Number=0)

TypeScript requires BOTH the ? at the end for optional AND the = for a default value if you pass nothing; ActionScript implies both:

// equivalent to above
constructor(x?:number=0, y?:number=0)
// optional only, no default value
constructor(x?:number, y?:number)
// default value, but required
constructor(x:number=0, y:number=0)

Autoset constructor conflict

Not sure if this is by design or a problem with the alpha compiler, but if you define your public properties and then define a constructor to automatically set them, the compiler bitches. Notice I have to comment out the public variables for it work with this constructor:

export class Circle implements IShape
{
	/*
	public x:number;
	public y:number;
	public width:number;
	public height:number;
	*/
 
	constructor(public x:number, public y:number, public width:number, public height:number)
	{
 
	}
}

Construct Signatures: constructor vs. new

A quick note about construct signatures. While you define constructor in your class if you wish, because TypeScript supports overloading, you can actually redefine multiple constructors simply by using new. It’s similar to how the Date object works in both JavaScript & ActionScript. You can pass in milliseconds into the first parameter only, or multiple parameters for the 2nd to get a explicit Date object using a specific year, month, etc.

Examples include:

new(x:number, y:number):Circle
{
	this.x      = x;
	this.y      = y;
	this.width  = 0;
	this.height = 0;
	return this;
}
 
new(radius:number):Circle
{
	this.x      = 0;
	this.y      = 0;
	this.width  = radius / 2;
	this.height = radius / 2;
	return this;
}

Keep in mind if you’re using version 0.8 of the tsc compiler, it may say “Duplicate identifier ‘new'” which is completely wrong, and it WILL generate the correct JavaScript.

NOTE: I cannot find a valid example of what you’re supposed to return. I’m not sure if it’s really supposed to be “return this;”, although it makes the compiler happy.

Inheritance

Not much to say here, it works as expected and provides a super keyword for you. You can use it in the constructor, to get base class properties, or call base class methods for overriding.

Here’s the ActionScript Greeting class:

package com
{
    public class Greeter
    {
        public var greeting:String;
 
        public function Greeter(message:String)
        {
            greeting = message;
        }
 
        public function greet():String
        {
            return "Hello, " + greeting;
        }
    }
}

And it’s equivalent TypeScript class:

export module com
{
    export class Greeter
    {
        greeting: string;
 
        constructor(message:string)
        {
            this.greeting = message;
        }
 
        greet()
        {
            return "Hello, " + this.greeting;
        }
    }
}

And now, to subclass in ActionScript:

package com
{
    public class UberGreeter extends Greeter
    {
        public function UberGreeter(message:String)
        {
            super(message + " da Uber");
        }
 
        public override function greet():String
        {
            return "UBER ZOMG Hello, " + super.greet();
        }
    }
}

And to subclass in TypeScript:

import GreeterModule = module("com/Greeter");
 
export module com
{
    export class UberGreeter extends GreeterModule.com.Greeter
    {
        constructor(message:string)
        {
            super(message + " ze Uber");
        }
 
        greet()
        {
            return "UBER ZOMG Hello, " + super.greet();
        }
    }
}

Notice a few things. First, we have to import the the module first, THEN use it for the extends. Since we don’t prefix “com/Greeting” with anything, it assumes the base directory (more on modules in the next section). Also, I’m using super in the constructor. Finally notice super.greet() in the overridden greet function. Unlike ActionScript, you don’t need to use the override keyword.

You only do import with modules; if you’re simply defining classes in global scope (ie not in a module keyword), you can just use them without importing them… assuming it’s in the global namespace/module. More on this in the next section.

Modules vs. Packages

Packages are at their basic level just ways to organize large code bases. Most people use ‘em for that + put that code into folders. You then use a file path to your class vs. just the class itself via an import/module/require type statement. Some languages like Ruby & Python allow you to even just import a function instead of the whole class.

ActionScript 3 had a few package specific features, but most people only used 1: put a class in it and use it. Most people put public in front of the class which means that class is accessible when you import the package. However, you could use private if you wish by NOT putting public in front of the class. Some people would do this for creating a private class, defined at the bottom of the package below the main class, to require inside of the constructor for Singleton enforcement. Others would do this for 1 time use Value Objects / DTO’s they didn’t want to make a formal class.

For JavaScript, this gets uber complex with low-level details that should NOT matter, but suffice to say, TypeScript works similarly to ActionScript in that you can define a module name like a package, and put classes in it. If you’re from Python, this should look familiar. Just remember to export both the class AND the module, like so:

export module com
{
    export class Greeter
    {
        greeting: string;
 
        constructor(message:string)
        {
            this.greeting = message;
        }
 
        greet()
        {
            return "Hello, " + this.greeting;
        }
    }
}

Notice I export the module as well as the class. This is used in another class like so:

import GreeterModule = module("com/Greeter");
// notice the module name is used
var instance2 = new GreeterModule.com.Greeter("tests");
// here, I just grab a class reference, like in Python or Lua
var Greeter = GreeterModule.com.Greeter;
var instance1 = new Greeter("tests");

Notice I snag a reference to the Module itself to make instantiating it easier. Following common package rules, my Greeter.ts file is in a folder called “com”.

Remember, you use a forward slash “/” for your module imports, NOT a dot “.”.

RequireJS and AMD

JavaScript doesn’t have packages and classes, RequireJS was created to give you that. It works great whether you’re using a bunch of external JavaScript files or all compiled to 1 file; it works the same. I’d argue most people reading this are thinking of using TypeScript for browser based apps. If so, you’ll be using RequireJS and its AMD plugin (asynchronous module definition)… unless you’re using AngularJS or some other framework that handles modules for you. TypeScript, if compiled with the –module AMD flag, will automatically export out your classes in the Require format. w00t!

In the example above, if you compile your TypeScript like this:

tsc Main.ts --module amd

Then you’ll get JavaScript like this:

define(["require", "exports", "com/Greeter"], function(require, exports, __GreeterModule__) {
    var GreeterModule = __GreeterModule__;
 
    var instance2 = new GreeterModule.com.Greeter("tests");
    var Greeter = GreeterModule.com.Greeter;
    var instance1 = new Greeter("tests");
})

Then you can just

require(["Main"]);

in your index.html.

If you don’t know RequireJS, all you have to know:

  1. require == import
  2. define == class
  3. Array of strings as first parameter to define == import dependency class
  4. 2nd parameter function to define == here are your classes loaded & ready to go

Scope: Bind vs. Arrow Function Expressions

In JavaScript it’s common to use function callbacks defined inline. In ActionScript, you typically pass in a class or point to a class member function. In ActionScript 1, scope was a major cause of confusion, so the Activation Object was used a lot to “keep scope”. We’d either use “theObject.owner = this” or the same thing they in JavaScript like var theXML = this; and then reference “theXML” in the callback function. In ActionScript 2, we just used mx.utils.Delegate. When ActionScript 3 came out and fixed this to have scope always be determined where the function was defined, it really screwed with those of us who were used to the status quo had the Mark & Sweeper from the new Garbage Collector unleashed on us.

JavaScript will typically handle this in 2 ways. They’ll either snag a reference to this:

var messenger =
{
   message: "Hello World",
   start: function()
   {var _this = this;
      setTimeout(function() { alert(_this.message); }, 3000);}};
messenger.start();

…which also helps _this compile smaller with JavaScript minifiers, or they’d use a bind function similar to Delegate:

var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
 
// and inside a class constructor:
function User()
{
   this.onError = __bind(this.onError, this);
   this.onSuccess = __bind(this.onSuccess, this);
   this.saveXml  = __bind(this.saveXml, this);
}

This way, whenever that class’ methods are used in a callback, scope is retained for this:

User.prototype.onSuccess = function(response)
{
   this.verifyNewUser();
};

CoffeeScript actually generates the above code. You can also use Underscore‘s bind.

CoffeeScript also uses the arrow => syntax to solve this and it’s making its way into the ECMAScript standard. TypeScript supports it as well. Here is the messenger example (from the TypeScript v0.8 language spec PDF btw) rewritten here:

var messenger =
{
   message: "Hello World",
   start: function()
   {
      setTimeout(() => { alert(this.message); }, 3000);
   }
};

Casting

When dealing with Factories that create sub classe types but treat it as an interface and/or base class, AND you know the type, you’ll want to cast it to ensure you get the proper code hinting/error checking. As of this writing, the code hints/intellisense only works on the TypeScript Playground and in Visual Studio. Cool as well is that TypeScript does a great job with type guessing that you often don’t need to do this, but it DOES make the code more readable because it makes it clear what your purpose ways.

In ActionScript, we snag some shapes in a drawing tool:

public interface IShape
{
	function get x():Number;
	function set x(value:Number):void;
 
	function get y():Number;
	function set y(value:Number):void;
 
	function get width():Number;
	function set width(value:Number):void;
 
	function get height():Number;
	function set height(value:Number):void;
 
	function draw():void;
	function move(x:Number, y:Number):void;
 
}
 
public class Circle implements IShape
{
 
}
 
public class Rectangle implements IShape
{
 
}
 
public class ShapeTypes
{
	public static const RECTANGLE:String 	= "rectangle";
	public static const CIRCLE:String 	= "circle";
}
 
function getShape(shapeType:String):IShape
{
	switch(shapeType)
	{
		case ShapeTypes.CIRCLE:
			return new Circle();
 
		case ShapeTypes.RECTANGLE:
			return new Rectangle();
	}
}
 
var square:IShape = getShape(ShapeTypes.RECTANGLE);
var circle:Circle = getShape(ShapeTypes.CIRCLE) as Circle;

Notice the first works fine. Although square is cast as an IShape, it’s actually a concrete type of Rectangle, but we don’t care; as long as he follows the IShape interface, we can use ‘em as such and the compiler will help here.

For the 2nd, if we wanted a concrete implementation, we have to cast our IShape to the concrete type, Circle, for this to work.

And in TypeScript:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
export interface IShape
{
	x:number;
	y:number;
	width:number;
	height:number;
	draw():void;
	move(x:number, y:number):void;
}
 
export class Circle implements IShape
{
	public x:number;
	public y:number;
	public width:number;
	public height:number;
 
	draw():void
	{
		// implementation
	}
 
	move(x:number, y:number):void
	{
		// implementation
	}
}
 
export class Rectangle implements IShape
{
	private _x:number;
	private _y:number;
	private _width:number;
	private _height:number;
 
	// NOTE: This only works with -ES5 or greater
	public get x():number { return this._x; }
	public set x(value:number)
	{
		this._x = value;
	}
 
	public get y():number { return this._y; }
	public set y(value:number)
	{
		this._x = value;
	}
 
	public get width():number { return this._width; }
	public set width(value:number)
	{
		this._width = value;
	}
 
	public get height():number { return this._height; }
	public set height(value:number)
	{
		this._height = value;
	}
 
	draw():void
	{
		// implementation
	}
 
	move(x:number, y:number):void
	{
		// implementation
	}
}
 
export class ShapeTypes
{
	static RECTANGLE:string = "rectangle";
	static CIRCLE:string    = "circle";
}
 
function getShape(shapeType:String):IShape
{
	switch(shapeType)
	{
		case ShapeTypes.RECTANGLE:
			return new Rectangle();
 
		case ShapeTypes.CIRCLE:
			return new Circle();
 
	}
}
 
var square:IShape = getShape(ShapeTypes.RECTANGLE);
var circle:Circle = <Circle> getShape(ShapeTypes.CIRCLE);

The key here is the last line, 92, where I cast it manually just like you have to do in ActionScript. The compiler will, rightfully, complain if you don’t. Notice it’s on the left side, not the right.

You’ll notice on line 36, I’ve used getter/setters, called “accessors” in TypeScript. You cannot define accessors in interfaces, but I DO have the option of implementing public properties or accessors. However, also note this is ONLY supported if you target ECMAScript 5 via the “–target ES5″ parameter to the tsc compiler. If you do that, it’ll generate the getter and setter methods like so in JavaScript:

Object.defineProperty(Rectangle.prototype, "x", {
    get: function () {
        return this._x;
    },
    set: function (value) {
        this._x = value;
    },
    enumerable: true,
    configurable: true
});

More About Operators and Types

Remember, TypeScript is JavaScript with language extensions. I haven’t covered operators (++, –, etc) nor anything special about “null” and “undefined”… because there isn’t anything special. While CoffeeScript and other languages fix and null and undefined to actually work like you expect, TypeScript does not. When in doubt, pretend you’re writing JavaScript. Because you are.

For extra confirmation, ensure you’re still putting “use strict”; in your exported JavaScript, as well as using JSLint and JSHint on the exported content.

Typed Array’s

JavaScript’s Array’s are just like ActionScript 1 and 2’s. You can use normal Array methods on it, but also treat as an Object and assign random stuff to it. This drove C and Java guys nuts when they started learning ActionScript and wondered why myArray.cow = “cheese” didn’t change trace(myArray.length) from printing out 0, etc.

TypeScript DOES have support for ensuring typed Array’s at compile time, similar to Vector’s in ActionScript which is hot. Again, it’s not enforced at runtime, but we’re just caring about compile time for now. Just keep that in mind when I compare it to the ActionScript 3 Vector API’s below. Yes, Microsoft claims they’re working on making these basically generics.

ActionScript 3:

var names:Vector.<String>;

TypeScript:

names:string[];

I basically remember the syntax by saying it out loud, “a string array”.

Why Not CoffeeScript?

CoffeeScript doesn’t offer strong-typing. TypeScript does. That’s all there is to say.

Why Not Google’s Dart?

I’m still keeping tabs on Dart. It’s really dope as well. Now that they have JavaScript integration and it has an Eclipse based IDE that works good on Mac, there is a ton of promise there.

The challenge with Google’s Dart, along with the awesome Haxe, is getting client buy in. A lot of JavaScript developers are racist against CoffeeScript. Some clients worry about hiring; can they find a CoffeeScript developer to maintain the code? What about hiring a JavaScript developer who wants to deal with the CoffeeScript? It gets harder when you start talking about an unfinished language like Dart (yes I know they’re close), an open source one that doesn’t have a lot of JavaScript developer buy in (Haxe), or a Microsoft specific compiler one (TypeScript).

At least Dart is backed by Google. They made Android. You saw what happened there. Yes, while people still love to bash Java, Android has surpassed iOS marketshare for devices. That’s huge. Google is the 1st major vendor to say, “JavaScript is not the best language for developing web applications so we’re going to build one.”

None of which covers the development & build stack. In Flash & Flex you only had 2 compilers, usually run client-side in either the IDE or command line, automated via either ANT or Maven. The JS world is waaaayyyy more diverse. Once you integrate a custom compiler, the rabit hole deepens to Abyss caliber. What stares back isn’t always consulting opportunities.

Jury’s out.

If you’re interested, the creators of Dart & TypeScript had an interview done together recently you can check out.

Why TypeScript again?

Microsoft is the 2nd. Sort of. They acknowledge that JavaScript’s lack of typing is NOT a good feature for building larger applications. They acknowledge Node’s impact on the web development landscape and that JavaScript is now the language to run all 3 development tiers: client, server, and build systems. This jives well with their Windows 8 philosophy of building windows applications using web tooling. That and it showcases the continual hotness that is Visual Studio.

The rest of us just benefit from being able to use ECMAScript 6 today… or having strong-typing in JavaScript, whatever makes you feel better.

Conclusions

What IS clear is that TypeScript is an easier sell to clients, both those building consumer products and those Enterprise customers: it’s already JavaScript, works with JavaScript, you can implement as much as you want. They follow ECMAScript 6, support v3, and v5, as well as the CommonJS and RequireJS modules which basically covers the entire JavaScript development world. This is all open source.

It’s primary selling point to clients is that it’s Microsoft backed. For developers it’s JavaScript with typing. The ability to have definition files quickly add strong-typing to existing JavaScript libraries is a major cool factor that lessens the cost of entry and solves the integration issues that arise with existing libraries.

For me, unit tests should not be “required” to build any application of reasonable size. Additionally, if they’re primary usage is to help with spelling errors and type casting errors EVEN AFTER using the IDE tools… there’s clearly a problem.

It currently feels like TypeScript fixes those problems. Hopefully JetBrains can get TypeScript support in WebStorm/IntelliJ (your vote counts) and the Sublime plugins can be improved, then developing on a Mac will more of a joy.

Learn more about TypeScript at TypeScriptLang.org and play with the language at the Playground.

23 Responses

  1. Philippe

    Another selling point for TypeScript imho: the generated JS is clean enough to be delivered as-is and maintained by a regular JS dev.

    Type to create “ambients” for CreateJS now ;)

  2. Rick

    How does debugging work with TypeScript? Can you run your page through Visual Studio and use breakpoints?

    One of the pains of coffee script, imo, is having to debug a large app, code/breaks in the browser isnt the same as your coffee script

    • Visual Studio I believe has an alpha version of source maps, yes. For the rest of us, it’s like CoffeeScript: “I write in 1 language, and debug in another, lol!”

    • Seriously though, I hear you. SourceMaps are already in nightlys of Chrome, fixin’ to come to Mozilla/Firefox soon, and if TypeScript has source maps, then I’m sure this’ll just magically work with IE9/IE10 as well.

  3. Piergiorgio Niero

    mmm… why not using haxe?

    • The challenge with Google’s Dart, along with the awesome Haxe, is getting client buy in.

      I’m a consultant. I work with large companies. I cannot usually dictate the stack. If I make a suggestion/professional recommendation, I need to be cogniscient of what resources the client currently has hired, who they need to hire, and how that technology choice fits into their current stack. This, against do I actually like the stack and can my team be effective in it.

      Basically, can I build what I need to build in it, and will my client have buyer’s remorse?

      Most of my clients prefer vendor backed technologies; it’s “safer”. If something breaks, you have a contract that guarantee’s a company will resolve a problem. You don’t have that with a lot of open source. If there is a show stopping bug in RequireJS, I don’t have a phone # I can call with a ticket system, and time of resolution estimate.

      On the flip side, Java, Spring, Hibernate, BlazeDS, etc. prove that companies CAN use such solutions, they do provide value, and you don’t need a large vendor for success.

      Bottom line, Google & Microsoft are huge vendors. Microsoft has a massive consulting arm as well as a ton of partners worldwide. TypeScript is equipped well to be supported. Haxe, that’s all up to the community right now.

      Obviously if you’re startup, or run your own technology stack, none of the above matters to you and is irrelevant, especially if you’re aiming for an early exit.

      “We wrote our web stack in Jade, Handlebars, and Closure.”

      “How are you going to hire for that tech-stack?”

      “Eh? We’re just going to sell to Yahoo and let them re-write everything in ColdFusion.”

  4. Great post! TypeScript looks cool in how it solves many JS problems. One concern… I looked at the Simple Inheritance example at the Playground link you provided. The JavaScript code generated seems like it would be more difficult to debug/maintain in its JavaScript form.

    • It’s not bad, I debug code like that all day. Just make room for your breakpoints to land in Safari/Chrome, and you’re good to go. Does the landing strip scare you? Sure, but wrapped in a try/catch, you’re good to go!

      • Landing strip?

        • Yeah, like some place where Chrome/Safari can realistically land a breakpoint.

          Bad:

          var o, k, moo, jQuery;

          or:

          if(__hasProp.call(parent, key)) child[key] = parent[key];

          Good would be:

          try
          {
             if(typeof error != 'undefined')
             {
                var errorCode = error.errorCode; // <-- // debug goes HEAHR
             }
          }

          There, you can safely debug AND the surrounding code won’t screw you up when entering and leaving, nor in the case of an explosion (exception). That is a pleasant place to debug.

  5. Thanks Jesse, one thing though..

    By not denoting a class as public it will become internal, not private. Private classes are not allowed in AS 3.0, using the private keyword on a class will give you the following error:

    “The private attribute may be used only on class property definitions.”

    That said by creating an internal class outside the package in the same .as file, nothing else outside the file has access to it, so you could deem it as private.

    • That’s what I meant.

  6. Haxe to Typescript baby. PROBLEM SOLVED Y’ALL

  7. Great writeup and read! Thanks for putting the time into this.

    • Thanks brudda!

  8. Great overview, thanks for the 4 step review of RequireJS too. NomNomNom, very digestible.

  9. Great article and TypeScript is brilliant and much needed.
    One possible correction: In AS3:

    public class UberGreeter extends Greeter

    is missing

    Thank you,

    J.

    • Thanks! Fixed.

  10. [...] suggest watching the video and then reading my partner Jesse Warden’s detailed post “TypeScript for ActionScript Developers“. If you want the 10,000 foot view from POV, here ya [...]

  11. Wait – how come I’ve been able to test cow==null in JS all this time with no face explosions? Am I missing a hidden landmine?

    • It’s for when you forget to declare, or the code does. If you’re dealing with awesome code you wrote, you’re fine using != null. However, if you’re dealing with legacy/someone elses code like I always do, or you’re just uber paranoid, use typeof. More info here:

      http://stackoverflow.com/questions/2703102/typeof-undefined-vs-null

    • Another is when dealing with global variables people put on window. Instead of going window.logger, say in the case of log4javascript, you can instead just use logger since it’s global. HOWEVER, in larger applications where you’re dealing with race conditions, say some Backbone View’s expecting to have references to Models that haven’t been instantiated yet… you can safely test for those globals via typeof and fall back with logging statements in production code. If you don’t, you’ll get a null pointer.

      Seems benign, but it’s not when refactoring spaghetti code that you want to ensure doesn’t break one thing when you fix another.

  12. Holy shit. Amazing write up – thanks!

  13. aYo

    thanx for the heads up Jesse.

  14. [...] on jessewarden.com Share this:TwitterFacebookLike this:LikeBe the first to like [...]

  15. [...] detailed explanation of how TypeScript works, I suggest you check out Jesse Warden’s excellent post on it. I also wanted to thank Richard Davey for pointing me in the right direction and answering my [...]

  16. [...] TypeScript for ActionScript Developers – back-to-back comparison with AS2/AS3 [...]