TypeScript学习笔记一:简介(译)

TypeScript简介

 TypeScript(以下简称ts)是由微软开发的自由和开源的编程语言,它是为满足建立和维护大型的js程序而设计的,它能帮助开发团队定义软件组件之间的接口,深入了解已存在的js库。ts可通过把代码组织到可动态加载的模块中来降低命名冲突。ts的可选类型系统可帮助js程序员使用静态检测、基于符号导航、语句完成和代码重构等高效开发工具实现快速编码

  ts是js的语法糖。它是ECMAScript 2015的一个超集。每一个js程序也是一个ts程序。ts编译器只将本地文件转换为ts程序而不对已声明的变量重新排序,这保证了js输出与ts输入紧密匹配。ts不改变变量名,能直接调试生成的js代码。ts提供了可选的源码映射以实现源码级调试。ts工具时在文件保存,保存测试,编辑时生成js代码。

  ts的语法包含ECMAScript 2015的所有特性,包括类和模块,并且兼容ECMAScript 3和ECMAScript  5。  

  类使我们可以用标准的方式来表达普通的面向对象模式,这让诸如继承等特性更具可读性和可互操作性。我们可以用模块(Module)来组织代码到组件当中以避免命名冲突。ts编译器提供模块代码生成选项以支持静态或动态模块内容加载。 ts还提供了一个可选的类型注释系统。这些类型注释类似于闭包系统当中的JsDoc注释,但不同的是,ts直接将其集成到语法中。这种集成使代码更具可读性,并降低了变量与相应类型注释同步的成本。ts的类型系统使用了类型推断。例如, ts可以从下面的语句推断出变量“i“的类型为”number“类型。

var i=0;

 同样地,ts将从下面的函数定义中推断出函数“f”的返回类型为“string”类型。此例我们并未提供类型注释。

function f() { 
    return "hello";
 }

  代码编辑器可以利用ts的类型推断服务来找到字符串对象的成员,如下面的屏幕截图。在ts中,我们可以使用下面的方式来表示参数的类型

function f(s:string){
    return s;
}
f({});        //error,参数类型不匹配
f('hello');    //ok

  这种可选的参数类型注释使ts类型检查器知道我们所期望的参数"s"的类型是"string类型。在函数f的函数体中,工具可以假设s的类型为string类型,然后通过运算符类型检查提供和与此假设相一致的成员。此例,ts编译器将生成下面的js代码: 

function f(s) {
  return s;
}

  我们可以看到,输出的 js代码的所有类型的注释已被擦除。

1.1 环境声明(Ambient)

  环境变量声明将变量引入ts域(scope),但不影响已经生成的JavaScript程序。我们可以使用环境声明告诉ts编译器某个组件将提供一个变量。默认情况下,ts编译器在使用未定义变量是会提示错误。 我们可以使用环境声明来添加一些常用的浏览器变量定义。 下面的示例声明了浏览器提供的“document”对象。 因为声明没有指定类型,因此推断该类型为“any”。类型“any”意味着工具可以对文档对象的形状或行为不做任何假设。 下面的一些例子将说明如何使用类型来进一步描述对象的预期行为。

declare var document; 
document.title = "Hello";
  此例中,ts编译器自动提供了一个声明,因为ts默认包含一个提供了内置js库和DOM的接口声明的‘lib.d.ts’文件。
  ts编辑器没有包含JQuery接口,因此如果要使用jquery,就得提供一个声明:
declare var $;

1.2 函数类型(Function Type)

  js中的函数表达式通过函数定义来创建闭包:此类函数从定义的范围中捕获信息。闭包是目前js的唯一数据封装方法。 通过获取和使用环境变量,闭包内的信息将不能被闭包外的成员访问。js程序员通常用闭包来表示事件处理和异步回调,ts函数类型使我们可以表达所期望的函数签名。函数签名包含参数类型序列和一个返回类型。下面的例子用函数类型来表示一个异步投票系统所需的回调签名。
function vote(candidate: string, callback: (result: string) => any) {
// ...
}
vote("BigPig", function(result: string) {
    if (result === "BigPig") {
    // ... 
    }
} );
  此例中,函数"vote"的第二个参数的类型就是“function"类型。
(result: string) => any
  第二个参数是一个函数,一个类型为string的参数和类型为'any'的返回值

1.3 对象类型(Object Type)

  我们可以用对象类型来声明期望的对象行为。下面的例子中"MakePoint"变量的类型为“function”类型,此函数的返回值为对象类型。
var MakePoint: () => { x: number; y: number; };
  我们可以给对象类型命名,命名的对象类型被称之为接口(interface)。下面的接口声明了一个必需的"name"字段和可选的"favoriteColor"字段。
interface Friend {
     name: string; 
   favoriteColor?: string; } function add(friend: Friend) { var name = friend.name; } add({ name: "Fred" }); // Ok add({ favoriteColor: "blue" }); // Error, name缺失 add({ name: "Jill", favoriteColor: "green" }); // ok
   ts对象类型模拟了js对象的行为多样。例如JQuery库定义了一个名为“$"的对象,它有"get"(发送ajax信息)方法和变量brower(提供浏览器供应的信息),然而,“$"也可以作为方法被调用,函数的行为依赖于传递给它的参数类型。下面是"JQuery.d.ts"文件中的一段代码。
interface JQuery { 
    text(content: string); 
}
interface JQueryStatic { 
    get(url: string, callback: (data: string) => any);
    (query: string): JQuery; 
}

declare var $: JQueryStatic;

$.get("http://mysite.org/divContent",
    function (data: string) { 
        $("div").text(data); 
} );    
  JQueryStatic接口引用了JQuery接口。这个接口代表了一个或多个DOM元素集合。在此例中,JQuery接口可以通过向“text”方法传递一个”string"类型的参数为集合中的每个元素设置文本内容。JQueryStatic接口包含了一个“get"方法来表示ajax请求,同时还包含了一个裸露函数签名:
(query: string): JQuery;
  裸露签名意味着接口的实例可被调用。此例说明ts的”function“类型是“object”类型的一个特例,具体而言,”function"类型是包含了一个或多个调用签名的“object”类型。
var f: { (): string; };
var sameType: () => string = f;// Ok
var nope: () => number = sameType; // Error: 类型不匹配
  我们注意到上面的“$"函数因为参数的类型而呈现出不同的行为。到目前为止,我们的jQuery类型只捕获一个行为:返回一个须传递"string"类型参数的类型为“JQuery”的对象。为了指定多个行为,ts支持函数签名重载。例如,我们可以为”JQueryStatic”接口添加一个额外的调用签名:
(ready: () => any): any;
  此签名表示函数可以作为参数传递。因为ts支持重载,所以工具可以显示所有可用的函数签名文档提示,当函数被作为特殊签名调用时将给出正确的文档提示。

1.4结构分型(Structural Subtyping)

  对象类型的比较是结构比较。例如下面的例子,类“CPoint”与接口“Point”相匹配,因为"CPoint"包含"Point"全部的成员。一个类可以可选的声明实现一个接口,编译器将检测声明是否结构兼容。
interface Point { 
    x: number; y: number;
 }
function getX(p: Point) {
     return p.x; 
}
class CPoint {
     x: number; y: number;
     constructor(x: number, y: number) {
        this.x = x; this.y = y; 
    } 
} 
getX(new CPoint(0, 0)); // Ok
getX({ x: 0, y: 0, color: "red" }); //ok
getX({ x: 0 }); // Error: 参数不匹配 

1.5 上下文类型(Contextual Typing)

  通常,ts类型推断是从表达式树的叶子到根的“自下而上”方式。下面的例子ts通过返回表达式是的“自下而上”类型信息而推断“mul”函数的返回类型为"number":
function mul(a: number, b: number) {
    return a * b;
 }  
  没有类型注释或默认值的变量和参数,ts推断其类型为“any”。通常,这种“自下而上”的推断方式能使我们对类型信息流有一个清晰的感知。 然而,在某些特定情况下,推断是从一个表达式的上下文中“自上而下”进行的,这种情况被称为上下文类型。当我们不知道类型的细节的时候 上下文类型能提供相应的信息。例如在上面的JQuery实例中,我们为“get”方法提供了一个函数表达式,在键入函数表达式时,工具可以假设 "get"签名中已经给定了此函数表达式类型并且提供一个包含参数名和参数类型的模板。
$.get("http://mysite.org/divContent",
    function (data) {
         $("div").text(data); // ts推断data类型为“string” 
} );  

1.6 类(Classes)

  js包含两种常见的设计模式:模块模式和类模式。大致说来,模块模式使用闭包来隐藏命名和包装私有数据, 类模式使用原型链来实现面向对象的继承机制,如典型的'prototype.js'库。ts命名空间形式化了模块模式。 下例是类的声明,我们声明了一个名为“BankAccount”的类:
class BankAccount {
     balance = 0;
     deposit(credit: number) {
         this.balance += credit; 
        return this.balance;
 } }    
“BankAccount”类生成的js代码如下:
var BankAccount = (function () { function BankAccount() {
     this.balance = 0;
 }

 BankAccount.prototype.deposit = function(credit) {
     this.balance += credit; 
    return this.balance; 
}; 
    return BankAccount; 
})(); 
上面的ts类声明创建了一个名为“BankAccount”的变量,我们也可以用接口方式表示“BankAccount”:
interface BankAccount {
     balance: number; 
    deposit(credit: number): number; 
}
  如果我们写出“BankAccount"构造器变量的函数声明,它将是这样的:
var BankAccount: new() => BankAccount;
  此函数签名以”new”关键字为前缀,意味着“BankAccount"函数必须作为构造器来调用。函数类型可用同时拥有调用和构造器签名, 例如内置的”Data"对象。
  我们可以为“BankAccount”类声明一个构造器:
class BankAccount { 
    balance: number; 
    constructor(initially: number) { 
        this.balance = initially; 
    }
    deposit(credit: number) { 
        this.balance += credit; 
        return this.balance; 
    } 
}
  此版本的“BankAccount”类需要我们引入一个构造器变量然后赋值给"banlance“字段。下面是简化的构造器方法。
class BankAccount {
     constructor(public balance: number) { } 
    deposit(credit: number) {
        this.balance += credit; 
        return this.balance;
 }
 }        
   ”public"关键字标准构造器的参数将被保留为一个字段,类成员的访问权限默认为”public“,访问权限是设计(design-time)时的, 它在静态类型检测时执行,而在运行时则不起作用。
   ts类支持继承,如下例:
class CheckingAccount extends BankAccount { 
    constructor(balance: number) { 
        super(balance);
    } 

    writeCheck(debit: number) {
     this.balance -= debit;
     }
 }    
  此例中”CheckingAccount“派生于”BankAccount“类。”CheckingAccount“构造器利用”super“关键字来调用”BankAccount“构造器。 在生成的js代码中,”CheckingAccount“的原型将链接至”BankAccount“的原型。
  ts类也可以指定静态成员,静态类成员成为类构造器原型。

1.7 枚举类型(Enum Types)

枚举类型包含了数字常量集合。如下例:
const enum Operator {
     ADD, DIV, MUL, SUB 
} 

function compute(op: Operator, a: number, b: number) { 
    console.log("the operator is" + Operator[op]); // ... 
}

 此例中,”Operator“声明为枚举成员列表自动分配一个以零开始的整型值。枚举声明也可以为枚举成员显示指定整型值。

  当枚举声明被”const"修饰符修饰时,ts编译器为枚举成员生成的js代码是枚举成员的值。如下“compute"函数:
switch (op) { 
  case Operator.ADD: 
  // execute add
  break;   case Operator.DIV:
  // execute div
  break; // ... }

生成的js代码如下:
switch (op) { 
    case 0 /* Operator.ADD */: 
    // execute add 
    break;
     case 1 /* Operator.DIV */:
     // execute div 
    break; // ... 
}

  

posted @ 2016-01-07 15:29  leeberg  阅读(527)  评论(0)    收藏  举报