第二章 Typescript

注:学习使用,禁止转载

angular 2是由Typescript编写

angular 2是有一门像Javascript的语言Typescript编写的。
你可能会觉得奇怪,angular 2为什么使用一门新的语言。但是,最终证明这是一个伟大的决定,这里有许多理由去使用Typescript而不是JavaScript。
Typescript不完全是一门新的语言,它是ES6的超集。如果你写过ES6的代码,它是完全有效并且兼容Typescript的。这里有一张图展示了这些语言之间的关系:

第二章 Typescript_第1张图片

:fa-info-circle: ES5、ES6是什么?ES5是“ECMAScript5”的简称,它是被叫做“正规JavaScript”而出名,ES5是我们知道并且喜欢的通常的JavaScript,ES6是下一代JavaScript。我们下面会经常讨论到。

在这本书发布的时候,只有少数浏览器能够运行ES6,更不用说Typescript了,所以我们需要一个翻译器。它将Typescript翻译为ES5,从而使得所有的浏览器都能够理解。

:fa-info-circle: 为了将Typescript转换成ES5,Typescript团队编写了一个简单的翻译器。然而,如果你希望将ES6代码转换成ES5而不是Typescript,这里有两个选择:Google出品的traceur和JavaScript社区出品的babel。在这本书中,我们不会去直接使用它们,但是它们都是值得了解的好的项目。

Typescript是Google与Microsoft合作的一个项目成果。这是一个激动人心的消息,因为这意味着它背后有两家顶级的科技公司提供长期的支持,不仅会推动web的发展,而且我们也会从中受益。

关于翻译器的一个意义就是,它只要求一个小的团队来改进语言,而不需要众多的用户来升级它们的浏览器。

有一点必须指出:我们不是必须用Typescript来编写angular2的应用程序。如果你想用ES5来写,完全没有问题。为什么我们会使用Typescript呢?因为它有很多特性来提供我们的效率。

我们可以从Typescript中得到什么

相对于ES5来说,Typescript有下面五个大的提升:

  • 类型(types)
  • 类(classes)
  • 注解(annotations)
  • 导入(import)
  • 语言工具(比如:解构)

让我们来详细了解一下。

类型(types)

Typescript相对于ES6来说主要的提升就是类型系统。
对于一些人来说,类型的缺失是JavaScript的好处,对于类型检查你可能持怀疑太多,但是我们建议你给它一个机会,类型检查最主要的好处如下:

  • 通过编译时检查它可以帮助在编写代码的时候预防bug
  • 阅读代码的时候更加清楚

值得注意的是,在Typescript中,类型是可选的。如果我们需要一个快速的原型,可以删除类型信息,在将来的时候慢慢加上。

Typescript的基本类型跟我们JavaScript中的类型是相同的,比如:string、number、boolean等。

直到ES5,我们定义类型通过var关键字,比如:var name;

Typescript的变量定义来源于ES5,但是我们可以提供那个变量的类型,在它的名字后面加上冒号,再加上类型信息。

var name:string;

当声明函数的时候,我们可以为参数与返回值添加类型.

function greetText(name:string):string{
    return "Hello " + name;
}

在上面的例子中,我们定义了一个新的函数greetText,它带一个string类型的name的参数,并且返回string.注意name:string代表的是name参数的类型是string,在括号后面的:string代表的是这个函数的返回类型是string.如果我们调用greetText传递的不是一个string参数,在编译不会通过.这个是我们希望的,不然它会导致一个潜在的bug.

让我们看看,如果你写的代码与我们声明的类型不一样会导致什么问题:

function hello(name: string): string { 
    return 12;
}

如果你试着编译它,你可以得到下面的错误:

$ tsc compile-error.ts
compile-error.ts(2,12): error TS2322: Type 'number' is not assignable to type 'string'.

发生了什么?我们试着返回的12是一个number类型,但是hello定了一个string的返回类型.

为了修正这个错误,我们可以将12转换成一个string,或者将函数声明修改为返回number,如下:

function hello(name: string): number { 
    return 12;
}

这是一个简单的例子,但是也可以看到,通过使用类型,在前期可以为我们发现很多的bug.

到现在为止,我们知道了怎么使用类型,我们可以使用的类型有哪些呢?先让我们了解一下內建类型,然后我们学习怎么创建自定义的类型.

使用REPL来联系

为了演示这一章的小例子,我们介绍一款工具,叫TSUN(TypeScript Upgrade Node):

$ npm install tsun

现在,启动tsun:

$ tsun
TSUN : TypeScript Upgraded Node
type in TypeScript expression to evaluate
type :help for commands in repl

> 

出现>符号说明tsun已经准备好了,下面的绝大多数例子我们都可以通过复制粘贴到这个终端运行.

内建类型

string

代表一个字符串:

var name: string = 'Felipe';

number

代表数字,所有的数都是使用浮点数表示:

var age: number = 36;

boolean

代表true与false两个数

var married: boolean = true;

Array

数组,我们也需要标明数组元素的类型,可以使用Array或者Type[]两种方式来表示:

var jobs: Array<string> = ['IBM', 'Microsoft', 'Google'];
var jobs: string[] = ['Apple', 'Dell', 'HP'];
var jobs: Array = [1, 2, 3];
var jobs: number[] = [4, 5, 6];

enum

enum是一个枚举,它使用命名参数来代表数值.比如,我们想要有一个角色列表,我们可能会像下面这样写:

enum Role {Employee, Manager, Admin};
var role: Role = Role.Employee;

enum默认的初始值为0,你也可以标明开始值:

enum Role {Employee = 3, Manager, Admin};
var role: Role = Role.Employee;

在上面的例子中,Employee不是0,而是3,后面的跟随定义的值相应增加1.也就是说,Manager是4,Admin是5.当然,你也可以为每一个单独设置一个值:

enum Role {Employee = 3, Manager = 5, Admin = 7};
var role: Role = Role.Employee;

你也可以寻找给定枚举的名字而不是它的值:

enum Role {Employee, Manager, Admin};
console.log('Roles: ', Role[0], ',', Role[1], 'and', Role[2]);

Any

any是一个默认类型,如果不需要标明变量的类型的时候.一个变量如果是any,表示它可以接收任何类型的值.

var something: any = 'as string';
something = 1;
something = [1, 2, 3];

Void

void表示没有值,这个通常使用在函数没有返回值的情况:

function setName(name:string):void{
    this.name = name;
}

Classes

在ES5中,面向对象的编程是通过基于原型的对象完成的.这个模型不适用类,但是依赖的是原型.
但是在ES6中,类被内建支持了.为了顶一个类,我们可以使用class关键字和类的名字及类的主题组成:

class Vehicle { 

}

类可以有属性,方法及构造器.

属性(properties)

属性定义了依附在类实例上的数据,比如一个Person类可能有firstname,lastname和age属性.可以定义如下:

class Person {
    firstName:string;
    lastName:string;
    age:number;
}

方法

方法是一个类实例上面可以操作的行为.比如:

class Person {
  first_name: string;
  last_name: string;
  age: number;
  greet() {
      console.log("hello " + this.firstName);
}
}

注意,我们可以使用this关键字去访问firstName,如果没有标明函数的返回类型,则代表可以返回任意类型,本例子中,就是返回void,也就是没有返回任意值.
要调用greet函数,你需要像下面这样:

// declare a variable of type Person
var p: Person;
// instantiate a new Person instance
p = new Person();
// give it a first_name
p.first_name = 'Felipe'; // call the greet method
p.greet();

构造器(constructor)

构造器是一类特殊的函数,它在类创建一个新实例的时候被调用,通常,构造器执行一些初始化的操作.
构造器函数必须被命名为constructor.它的参数是可选的,但是不能返回任何类型.
当一个类没有显示声明constructor时,我们会自动隐士创建一个:

class Vehicle{}
var v= new Vehicle()

它等同于:

class Vehicle{
    constructor(){
    }
}
var v= new Vehicle();

constructor可以添加任何的参数,比如:

class Person {
    first_name: string;
    last_name: string;
    age: number;
    constructor(first_name: string, last_name: string, age: number) {
        this.first_name = first_name;
        this.last_name = last_name;
        this.age = age;
    }
    greet() {
        console.log("Hello", this.first_name);
    }
    ageInYears(years: number): number {
        return this.age + years;
    }
}

继承

面向对象编程另一个重要的方面就是继承.继承是一个类从其父类接收数据和行为的一种方式.然后我们可以在新类中重载,修改或者增加这些行为.
Typescript提供了完整的支持,不像ES5,Typescript是内建支持的.重载时通过关键字extends完成的.
为了描述这个,看下面的例子:

class Report {
    data: Array<string>;
    constructor(data: Array<string>) { this.data = data; }
    run() {
        this.data.forEach(function (line) { console.log(line); });
    }
}

var r: Report = new Report(['First line', 'Second line']);
r.run();

这个程序会输出:

First line
Second line

现在,我们希望去实现一个头不和另外一些数据的报告,但是希望从Report类继承,我们可以像下面这样实现:

class Report {
    data: Array;
    constructor(data: Array) { this.data = data; }
    run() {
        this.data.forEach(function (line) { console.log(line); });
    }
}


class TabbedReport extends Report {
    headers: Array;
    constructor(headers: string[], values: string[]) {
        super(values);
        this.headers = headers;
    }
    run() {
        console.log(this.headers);
        super.run();
    }
}

var headers: string[] = ['Name'];
var data: string[] = ['Alice Green', 'Paul Pfifer', 'Louis Blakenship']; 
var r: TabbedReport = new TabbedReport(headers, data)
r.run();

工具

Typescript提供了很多语法特性来提供开发效率,最重要的两个就是:

  • 箭头函数(fat arrow function syntax)
  • 模板字符串

箭头函数

箭头函数是使用 => 来简化函数定义的一种方式.
在ES5中,不关我们是传递函数参数,必须使用function关键字和一对大括号{}

// ES5-like example
var data = ['Alice Green', 'Paul Pfifer', 'Louis Blakenship'];
data.forEach(function(line) { console.log(line); });

如果使用=>,我们可以像下面这样:

// Typescript example
var data: string[] = ['Alice Green', 'Paul Pfifer', 'Louis Blakenship']; data.forEach( (line) => console.log(line) );

=>可以作为表达式:

var evens = [2,4,6,8];
var odds = evens.map(v => v + 1);

或者作为语句:

data.forEach( line => {
  console.log(line.toUpperCase())
});

箭头函数的一个重要作用就是共享this,这个跟JavaScript不一样,如下:

varnate = {
    name: "Nate",
    guitars: ["Gibson", "Martin", "Taylor"], printGuitars: function () {
        var self = this;
        this.guitars.forEach(function (g) {
            // this.name is undefined so we have to use self.name
            console.log(self.name + " plays a " + g);
        });
    }
};

因为=>可以共享this,所以我们可以像下面这样:

varnate = {
    name: "Nate",
    guitars: ["Gibson", "Martin", "Taylor"], printGuitars: function () {
        this.guitars.forEach((g) => {
            console.log(this.name + " plays a " + g);
        });
    }
}

=>可以让你的内部函数更加清晰,它使得在JavaScript中使用高阶函数变得容易.

模板字符串

在ES6中,新的模板字符串被介绍,模板字符串有两个重要的特性:

  • 字符串中的变量
  • 多行字符串
字符串中的变量

字符串中的变量也称为”字符串插值”,你可以在你的字符串中插入变量的方式:

var firstName = "Nate";
var lastName = "Murray";

// interpolate a string
var greeting = `Hello ${firstName} ${lastName}`;

console.log(greeting);

记住,使用字符串插值必须使用反引号`,不是单引号或者双引号.

多行字符

另一个重要的特性就是多行字符:

var template = `
<div>
<h1>Helloh1>
<p>This is a great websitep>
div
`

当我们需要将strings放入我们的代码中的时候并且这个字符比较长的时候,多行字符提供了很大的帮助.

总结

Typescript/ES6有其他一些重要的特性:

  • 接口
  • 泛型
  • 导入和导出模块
  • 注解
  • 解构(destructuring)

在本书中,我们会碰到他们,但是现在,有这些基础知识就可以了.
现在,让我们返回angular的学习.

你可能感兴趣的:(angular2)