从 ActionScript3 到 TypeScript

以下是我总结的笔记,只涉及语法部分,有错漏请指正。

PS.我曾经用正则表达式写过一个AS3转TypeScript的在线转换器

基本类型

基础类型只有这么几种,与AS3对比

TypeScript ActionScript3
number Number
string String
boolean(0.8以前为bool) Boolean
any *
undefined undefined
null null

任意类型

ActionScript3 :

var anyType : * = ...;

TypeScript:

var anyType : any = ...;

变量修饰符

TS类似AS3 但不用写"var" , 没有protected (未来也许会添加) TypeScript 1.3 加入了protected关键字,与AS3用法一致,默认为public。

ActionScript3 :

private var str: String = "abc";
public  var num: Number = 123;
var num2 :Number = 456;
static var bo: Boolean = true;
public var createTime: String;

TypeScript:

private str: string = "abc";    // private property
public  num: number = 123;      // public property
num2 :number = 456;             // 不写默认也为public
static bo: boolean = true;         // static
public createTime: string;      //createTime 类型为string,值为 undefined

变量类型转换

ActionScript3 :

var str:String = "abc";
var strNum:Number = Number(str);
var strNum:Number = str as Number;

TypeScript :

var str : string = "abc";
var strNum: number = <number> str; //编译报错 Cannot convert 'string' to 'number'

发现使用any类型会编译通过:

var str2 :any = "abc"; //any类型
var strNum2 :number = <number> str2; //通过
strNum2 += 5; // abc5

用 instanceof 判断类型

// instanceof
function CalculateArea(shape : Shape) : number {
    if (shape instanceof Square) {
        return (<Square>shape).x * (<Square>shape).y;
    }
    if (shape instanceof Ellipse) {
        return (<Ellipse>shape).r1 * (<Ellipse>shape).r2 * Math.PI;
    }
    if (shape instanceof Triangle) {
        return 0.5 * (<Triangle>shape).x * (<Triangle>shape).y;
    }
    throw new TypeError("Unsupported type!");
}

数组

Typescript数组的写法比AS3漂亮很多

ActionScript3 :


var arr:Array = [1,2,3,"a","b","c"]; // 任意类型数组
var strArray:Vector.<String> = Vector.<String>(["a", "b", "c"]); //固定类型数组

TypeScript :


var arr: any[] = new Array(); // 任意类型数组
var strArr: string[] = ["a", "b", "c" ]; //固定类型数组

二维数组


var array2d: string[][] = [
    ["a", "b", "c"],
    ["x", "y", "z"]
];

// or

var array2d: string[][] = new Array();
array2d.push(["a", "b", "c"]);
array2d.push(["x", "y", "z"]);

Object Types

当AS3调用这样一个函数时,会有下面一个问题。

ActionScript3 :


function CalculateArea ( rect : Object ):Number
{
    return rect.width * rect.height;
}

此时编译器并不知道rect这个Object里到底有没有width和height,只能等到runtime时才会知道。

TypeScript 引入Object Types解决这一问题,可以指定Object参数的具体内容,相当于让Object参数实现了interface


function CalculateArea(rect: {width: number; height: number;}): number
{
    return rect.width * rect.height;
}

此时如果调用CalculateArea({w:123,h:456}); 编译器不会通过。

Object Types 还支持”?"表示可选参数


function CalculateArea(rect: {width:number; height:number; depth?:number;}): number
{
    if(rect.depth)
    {
        return rect.width * rect.height * rect.depth;
    }
    return rect.width * rect.height;
    }

    CalculateArea({w:123,h:456}); //编译器报错
    CalculateArea({width:123,height:456}); // 通过
    CalculateArea({width:123,height:456,depth:789}); // 通过

可以这样声明一个Object

    var example: {
        name: string;
        id: number;
        collection: string[]; 
    } = {
        name: 'Example',
        id: 5,
        collection: ['a', 'b', 'c']
    }

ActionScript3 :


    var fun:Function;
    var fun2:Function = someFunction;

TypeScript 可以指定Function需要的参数和返回值类型,叫做函数签名,所以变成了这样


    var fun : (str: string) => void;   // fun是输入为string,没有输出的函数
    var fun2 : (num: number) => number = someFnction;  //someFnction函数必须输入输出都为number类型

eg.指定callback函数为string输入,any输出。


function vote(candidate: string, callback: (result: string) => any) {
// ...
}

vote("BigPig",
    function(result: string) {
        if (result === "BigPig") {
            // ...
        }
    });

TypeScript 这样的语法虽然使Function更清晰了,但也带来麻烦,比如一个简单的输入输出都是string的函数就要写好长不易阅读


var sayHello: (input: string) => string = function (s: string) {
    return "Hello " + s;
}

//保存函数的数组也写很长
var strArray: { (s: string): string; }[] = [sayHello, function aa(str: string) { return str; }];    //两个输入输出都为string的函数

所以引入了 函数接口

函数接口


//定义输入输出都是字符串的函数的接口
interface IStringFunction {
    (input: string) : string;
}

上边很长的都可以这么写了


var sayHello: IStringFunction = function (s: string) {
    return "Hello " + s;
}
var strArray: IStringFunction[];
strArray.push(sayHello);

可选参数加"?"


//带默认值
function func (a: number, b?: bool = false): number
    {
    if (b) { return a + b };
    return a;
}

//不带默认值,要自己判断了
function func (a: number,b?: bool): number 
{
    if ( b !== null && b !== undefined )
    {
    if ( b) { return a + b };
    }
    return a;
}

rest参数 ...paramName[:paramType]


function CountDwarvesTallerThan(minHeight: number, ...dwarves: Dwarf[]) : number 
{
    var count: number = 0;
    for (var i = 0; i < dwarves.length; i++) {
        if (dwarves[i].height > minHeight) {
            count++;
        }
    }
    return count;
}

Arrow Function

关于this作用域的问题,AS1时代经常用到的一个技巧:


var _this = this

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

TypeScript把这个技巧封装到语言里了,叫Arrow Function

语法格式为 ()=>{},例子:

TypeScript:


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

编译后的JavaScript跟上边的一样


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

网上找到的一个例子,


//declare var 为环境声明,用来告诉编译器已知的变量类型,例如浏览器定义的一些变量类型

declare var menu: HTMLElement;       
declare var sideBar: HTMLElement;

class UITester {
    menuTouches: number;
    sidebarTouches: number;

    beginMenuTest(): void {
        this.menuTouches = 0;   // Right!!
        menu.onmouseenter = function (e) {
            this.menuTouches++;  // Wrong!! 
        }
    }

    beginSidebarTest(): void {
        this.sidebarTouches = 0;  // Right!!
        sideBar.onmousemove = e => {  
            this.sidebarTouches++;  // Still right!!
        }
    }
}

语法中用了(e)=>{} ,将e传给后边的函数,并且省略了e的括号,会编译成这样:


var UITester = (function () {
    function UITester() { }
    UITester.prototype.beginMenuTest = function () {
        this.menuTouches = 0;
        menu.onmouseenter = function (e) {
            this.menuTouches++;
        };
    };
    UITester.prototype.beginSidebarTest = function () {
        var _this = this;
        this.sidebarTouches = 0;
        sideBar.onmousemove = function (e) {
            _this.sidebarTouches++;
        };
    };
    return UITester;
})();

这里有篇教程详细解释了这个语法:

http://www.codebelt.com/typescript/arrow-function-typescript-tutorial/

类相关的

TypeScript中的module相当于ActionScript3中的Package

TypeScript中构造函数的函数名用constructor ,而不用类名。

TypeScript:


module net.nshen { 
    export class Test1
    {
        private str: string = "abc";    // private property
        public  num: number = 123;      //public property

        public createTime: string;      //createTime = undefined

        constructor() // constructor
        {
            this.createTime = new Date().toUTCString();
        }

        static traceDate(): void
        {
            var currentDate: Date = new Date(); 
            console.log(currentDate.toUTCString());
        }
    }
}

调用Static方法


net.nshen.Test1.traceDate();

module原理

module始终是要编译成JS代码的,写一个简单的module看一下原理:


module M {
    var s = "hello";
    export function f() {
        return s;
        }
}

M.f();
M.s;  // Error, s is not exported

编译后的JS代码


var M;
(function(M) {
    var s = "hello";
    function f() {
    return s;
    }
    M.f = f;
})(M||(M={}));

据说这是js界很流行的写法,叫做JavaScript module pattern

函数重载

AS3和JS都是不支持函数重载的,TypeScript以一种鸡肋的方式支持着。

先写一些同名的函数声明,最后在一个同名函数里写出实现(要自己判断参数类型):

TypeScript:


function attr(name: string): string;
function attr(name: string, value: string): Accessor;
function attr(map: any): Accessor;

function attr(nameOrMap: any, value?: string): any {
    if (nameOrMap && typeof nameOrMap === "object") {
        // handle map case
    }
    else {
        // handle string case
    }
}

最终会编译成一个JS方法:

JavaScript:


function attr(nameOrMap, value) {
    if (nameOrMap && typeof nameOrMap === "object") {
    } else {
    }
}

2014/12/07 补充 : js判断类型也是个很大的坑,详见 http://tobyho.com/2011/01/28/checking-types-in-javascript/

TypeScript 允许多个类在同一个文件里,但如果类与类在不同的文件,需要这种写法,相当于AS3 的 import


/// <reference path="SimpleWebSocket.ts"/>
class ComplexWebSocket extends SimpleWebSocket {
...
}

override方法子类不需要写关键字,直接同名方法即可 ,可调用super.xxx()


class Base {

    public test():number
    {
        return 1;
    }

    public test2():number
    {
        return 2;
    }
}

class Derived extends Base {

    public test():number
    {
        return 3;
    }

    public test2():number
    {
        return super.test();
    }

}

var d:Derived = new Derived();
console.log(d.test()); // 3
console.log(d.test2());// 1

Enum

TypeScript支持enum关键字


enum Color { Red, Green, Blue }
console.log(Color.Red); // 0
var c:number = Color.Green;
console.log(Color[c])  //Green

生成对应的js


var Color;
(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["Green"] = 1] = "Green";
    Color[Color["Blue"] = 2] = "Blue";
})(Color || (Color = {}));
console.log(Color.Red);
var c = Color.Green;
console.log(Color[c]);//Green

本文采用 署名-禁止演绎 4.0 国际许可协议 (CC BY-ND 4.0) 进行许可(保留链接可任意转载,禁止修改)。
TypeScript Flash

留言系统需要代理访问