typescript 中文手册

转自 zhongsp.gitbooks.io

基础类型 介绍

为了让程序有价值,我们需要能够处理最简单的数据单元:数字,字符串,结构体,布尔值等。
TypeScript支持与JavaScript几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。

布尔值

最基本的数据类型就是简单的true/false值,在JavaScript和TypeScript里叫做boolean(其它语言中也一样)。

var isDone: boolean = false;

数字

和JavaScript一样,TypeScript里的所有数字都是浮点数。
这些浮点数的类型是number
除了支持十进制和十六进制字面量,Typescript还支持ECMAScript 2015中引入的二进制和八进制字面量。

var decLiteral: number = 6;
var hexLiteral: number = 0x9837abdef;
var binaryLiteral: number = 0b0010;
var octalLiteral: number = 0o74563;

字符串

JavaScript程序的另一项基本操作是处理网页或服务器端的文本数据。
像其它语言里一样,我们使用string表示文本数据类型。
和JavaScript一样,可以使用双引号(")或单引号(')表示字符串。

var name: string = "bob";
name = "smith";

你还可以使用模版字符串,它可以定义多行文本和内嵌表达式。
这种字符串是被反引号包围(`),并且以${ expr }这种形式嵌入表达式

var name: string = `Gene`;
var age: number = 37;
var sentence: string = `Hello, my name is ${ name }.

I'll be ${ age + 1 } years old next month.`;

这与下面定义sentence的方式效果相同:

var sentence: string = "Hello, my name is " + name + ".\n\n" +
    "I'll be " + (age + 1) + " years old next month.";

数组

TypeScript像JavaScript一样可以操作数组元素。
有两种方式可以定义数组。
第一种,可以在元素类型后面接上[],表示由此类型元素组成的一个数组:

var list: number[] = [1, 2, 3];

第二种方式是使用数组泛型,Array<元素类型>

var list: Array = [1, 2, 3];

枚举

enum类型是对JavaScript标准数据类型的一个补充。
像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。

enum Color {Red, Green, Blue};
var c: Color = Color.Green;

默认情况下,从0开始为元素编号。
你也可以手动的指定成员的数值。
例如,我们将上面的例子改成从1开始编号:

enum Color {Red = 1, Green, Blue};
var c: Color = Color.Green;

或者,全部都采用手动赋值:

enum Color {Red = 1, Green = 2, Blue = 4};
var c: Color = Color.Green;

枚举类型提供的一个便利是你可以由枚举的值得到它的名字。
例如,我们知道数值为2,但是不确定它映射到Color里的哪个名字,我们可以查找相应的名字:

enum Color {Red = 1, Green, Blue};
var colorName: string = Color[2];

alert(colorName);

任意值

有时,我们可能会想要为那些在编写程序阶段还不清楚其类型的变量指定一个类型。
这些值可能来自于动态的内容,比如来自用户或第三方代码库。
这种情况下,我们不希望类型检查器对这些值进行检查或者说让它们直接通过编译阶段的检查。
那么我们可以使用any类型来标记这些变量:

var notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

在对现有代码进行改写的时候,any类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。
你可能认为Object有差不多的作用,就像它在其它语言中那样。
但是Object类型的变量只是允许你给它赋任意值 – 但是你不像在它上面调用任意方法,就算它真的包含了这些方法:

var notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)
var prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

当你只知道数据的类型的一部分时,any类型也是有用的。
比如,你有一个数组,它包含了不同的数据类型:

var list: any[] = [1, true, "free"];

list[1] = 100;

空值

某种程度上来说,void类型像是与any类型相反,它表示没有任何类型。
当一个函数没有返回值时,你通常会见到其返回值类型是void

function warnUser(): void {
    alert("This is my warning message");
}

声明一个void类型的变量没有什么大用,因为你只能为它赋予undefinednull

var unusable: void = undefined;

接口 介绍

TypeScript的核心原则之一是对值所具有的shape进行类型检查。
它有时被称做“鸭式辨型法”或“结构性子类型化”。
在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

接口初探

下面通过一个简单示例来观察接口是如何工作的:

function printLabel(labelledObj: { label: string }) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

类型检查器会查看printLabel的调用。
printLabel有一个参数,并要求这个对象参数有一个名为label类型为string的属性。
需要注意的是,我们传入的对象参数实际上会包含很多属性,但是编译器只会检查那些必需的属性是否存在,并且其类型是否匹配。

下面我们重写上面的例子,这次使用接口来描述:必须包含一个label属性且类型为string

interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

LabelledValue接口就好比一个名字,用来描述上面例子里的要求。
它代表了有一个label属性且类型为string的对象。
需要注意的是,我们在这里并不能像在其它语言里一样,说传给printLabel的对象实现了这个接口。我们只会去关注值的外形。
只要传入的对象满足上面提到的必要条件,那么它就是被允许的。

还有一点值得提的是,类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。

可选属性

接口里的属性不全都是必需的。
有些是只在某些条件下存在,或者根本不存在。
可选属性在应用“option bags”模式时很常用,即给函数传入的参数对象中只有部分属性赋值了。

下面是应用了“option bags”的例子:

interface SquareConfig {
  color?: string;
  width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
  var newSquare = {color: "white", area: 100};
  if (config.color) {
    newSquare.color = config.color;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

var mySquare = createSquare({color: "black"});

带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。

可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。
比如,我们故意将createSquare里的color属性名拼错,就会得到一个错误提示:

interface SquareConfig {
  color?: string;
  width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
  var newSquare = {color: "white", area: 100};
  if (config.color) {
    // Error: Property 'collor' does not exist on type 'SquareConfig'
    newSquare.color = config.collor;  // Type-checker can catch the mistyped name here
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

var mySquare = createSquare({color: "black"});

函数类型

接口能够描述JavaScript中对象拥有的各种各样的外形。
除了描述带有属性的普通对象外,接口也可以描述函数类型。

为了使用接口表示函数类型,我们需要给接口定义一个调用签名。
它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。

interface SearchFunc {
  (source: string, subString: string): boolean;
}

这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。
下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。

var mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
  var result = source.search(subString);
  if (result == -1) {
    return false;
  }
  else {
    return true;
  }
}

对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。
比如,我们使用下面的代码重写上面的例子:

var mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
  var result = src.search(sub);
  if (result == -1) {
    return false;
  }
  else {
    return true;
  }
}

函数的参数会逐个进行检查,要求对应位置上的参数类型是兼容的。
如果你不想指定类型,Typescript的类型系统会推断出参数类型,因为函数直接赋值给了SearchFunc类型变量。
函数的返回值类型是通过其返回值推断出来的(此例是falsetrue)。
如果让这个函数返回数字或字符串,类型检查器会警告我们函数的返回值类型与SearchFunc接口中的定义不匹配。

var mySearch: SearchFunc;
mySearch = function(src, sub) {
    var result = src.search(sub);
    if (result == -1) {
        return false;
    }
    else {
        return true;
    }
}

数组类型

与使用接口描述函数类型差不多,我们也可以描述数组类型。
数组类型具有一个index类型表示索引的类型,还有一个相应的返回值类型表示通过索引得到的元素的类型。

interface StringArray {
  [index: number]: string;
}

var myArray: StringArray;
myArray = ["Bob", "Fred"];

支持两种索引类型:string和number。
数组可以同时使用这两种索引类型,但是有一个限制,数字索引返回值的类型必须是字符串索引返回值的类型的子类型。

索引签名能够很好的描述数组和dictionary模式,它们也要求所有属性要与返回值类型相匹配。
因为字符串索引表明obj.propertyobj["property"]两种形式都可以。
下面的例子里,length的类型与字符串索引类型不匹配,所以类型检查器给出一个错误提示:

interface NumberDictionary {
  [index: string]: number;
  length: number;    // 可以,length是number类型
  name: string       // 错误,`name`的类型不是索引类型的子类型
}

类类型

实现接口

与C#或Java里接口的基本作用一样,TypeScript也能够用它来明确的强制一个类去符合某种契约。

interface ClockInterface {
    currentTime: Date;
}

class Clock implements ClockInterface {
    currentTime: Date;
    constructor(h: number, m: number) { }
}

你也可以在接口中描述一个方法,在类里实现它,如同下面的setTime方法一样:

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date);
}

class Clock implements ClockInterface {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}

接口描述了类的公共部分,而不是公共和私有两部分。
它不会帮你检查类是否具有某些私有成员。

类静态部分与实例部分的区别

当你操作类和接口的时候,你要知道类是具有两个类型的:静态部分的类型和实例的类型。
你会注意到,当你用构造器签名去定义一个接口并试图定义一个类去实现这个接口时会得到一个错误:

interface ClockConstructor {
    new (hour: number, minute: number);
}

class Clock implements ClockConstructor {
    currentTime: Date;
    constructor(h: number, m: number) { }
}

这里因为当一个类实现了一个接口时,只对其实例部分进行类型检查。
constructor存在于类的静态部分,所以不在检查的范围内。

因此,我们应该直接操作类的静态部分。
看下面的例子,我们定义了两个接口,ClockConstructor为构造函数所用和ClockInterface为实例方法所用。
为了方便我们定义一个构造函数createClock,它用传入的类型创建实例。

interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
    tick();
}

function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
    return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("beep beep");
    }
}
class AnalogClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("tick tock");
    }
}

var digital = createClock(DigitalClock, 12, 17);
var analog = createClock(AnalogClock, 7, 32);

因为createClock的第一个参数是ClockConstructor类型,在createClock(AnalogClock, 12, 17)里,会检查AnalogClock是否符合构造函数签名。

扩展接口

和类一样,接口也可以相互扩展。
这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。

interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

var square = {};
square.color = "blue";
square.sideLength = 10;

一个接口可以继承多个接口,创建出多个接口的合成接口。

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

var square = {};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

混合类型

先前我们提过,接口能够描述JavaScript里丰富的类型。
因为JavaScript其动态灵活的特点,有时你会希望一个对象可以同时具有上面提到的多种类型。

一个例子就是,一个对象可以同时做为函数和对象使用,并带有额外的属性。

interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}

var c: Counter;
c(10);
c.reset();
c.interval = 5.0;

在使用JavaScript第三方库的时候,你可能需要像上面那样去完整地定义类型。

接口继承类

当接口继承了一个类类型时,它会继承类的成员但不包括其实现。
就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。
接口同样会继承到类的private和protected成员。
这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。

这是很有用的,当你有一个很深层次的继承,但是只想你的代码只是针对拥有特定属性的子类起作用的时候。子类除了继承自基类外与基类没有任何联系。
例:

class Control {  
    private state: any;
}

interface SelectableControl extends Control {  
    select(): void;  
}

class Button extends Control {  
    select() { }  
}
class TextBox extends Control {  
    select() { }  
}
class Image extends Control {  
}
class Location {  
    select() { }  
}

在上面的例子里,SelectableControl包含了Control的所有成员,包括私有成员state
因为state是私有成员,所以只能够是Control的子类们才能实现SelectableControl接口。
因为只有Control的子类才能够拥有一个声明于Control的私有成员state,这对私有成员的兼容性是必需的。

Control类内部,是允许通过SelectableControl的实例来访问私有成员state的。
实际上,SelectableControl就像Control一样,并拥有一个select方法。
ButtonTextBox类是SelectableControl的子类(类为它们都继承自Control并有select方法),但ImageLocation类并不是这样的。

类 介绍

传统的JavaScript程序使用函数和基于原型的继承来创建可重用的组件,但这对于熟悉使用面向对象方式的程序员来说有些棘手,因为他们用的是基于类的继承并且对象是从类构建出来的。
从ECMAScript 2015,也就是ECMAScript 6,JavaScript程序将可以使用这种基于类的面向对象方法。
在TypeScript里,我们允许开发者现在就使用这些特性,并且编译后的JavaScript可以在所有主流浏览器和平台上运行,而不需要等到下个JavaScript版本。

下面看一个使用类的例子:

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

var greeter = new Greeter("world");

如果你使用过C#或Java,你会对这种语法非常熟悉。
我们声明一个Greeter类。这个类有3个成员:一个叫做greeting的属性,一个构造函数和一个greet方法。

你会注意到,我们在引用任何一个类成员的时候都用了this
它表示我们访问的是类的成员。

最后一行,我们使用new构造了Greeter类的一个实例。
它会调用之前定义的构造函数,创建一个Greeter类型的新对象,并执行构造函数初始化它。

继承

在TypeScript里,我们可以使用常用的面向对象模式。
当然,基于类的程序设计中最基本的模式是允许使用继承来扩展一个类。

看下面的例子:

class Animal {
    name:string;
    constructor(theName: string) { this.name = theName; }
    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}

class Horse extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 45) {
        console.log("Galloping...");
        super.move(distanceInMeters);
    }
}

var sam = new Snake("Sammy the Python");
var tom: Animal = new Horse("Tommy the Palomino");

sam.move();
tom.move(34);

这个例子展示了TypeScript中继承的一些特征,与其它语言类似。
我们使用extends来创建子类。你可以看到HorseSnake类是基类Animal的子类,并且可以访问其属性和方法。

这个例子演示了如何在子类里可以重写父类的方法。
Snake类和Horse类都创建了move方法,重写了从Animal继承来的move方法,使得move方法根据不同的类而具有不同的功能。
注意,即使tom被声明为Animal类型,因为它的值是Horsetom.move(34)调用Horse里的重写方法:

Slithering...
Sammy the Python moved 5m.
Galloping...
Tommy the Palomino moved 34m.

公共,私有与受保护的修饰符

默认为公有

在上面的例子里,我们可以自由的访问程序里定义的成员。
如果你对其它语言中的类比较了解,就会注意到我们在之前的代码里并没有使用public来做修饰;例如,C#要求必须明确地使用public指定成员是可见的。
在TypeScript里,每个成员默认为public的。

你也可以明确的将一个成员标记成public
我们可以用下面的方式来重写上面的Animal类:

class Animal {
    public name: string;
    public constructor(theName: string) { this.name = theName; }
    move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

理解private

当成员被标记成private时,它就不能在声明它的类的外部访问。比如:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

new Animal("Cat").name; // Error: 'name' is private;

TypeScript使用的是结构性类型系统。
当我们比较两种不同的类型时,并不在乎它们从哪儿来的,如果所有成员的类型都是兼容的,我们就认为它们的类型是兼容的。

然而,当我们比较带有privateprotected成员的类型的时候,情况就不同了。
如果其中一个类型里包含一个private成员,那么只有当另外一个类型中也存在这样一个private成员, 并且它们是来自同一处声明时,我们才认为这两个类型是兼容的。
对于protected成员也使用这个规则。

下面来看一个例子,详细的解释了这点:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

class Rhino extends Animal {
    constructor() { super("Rhino"); }
}

class Employee {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

var animal = new Animal("Goat");
var rhino = new Rhino();
var employee = new Employee("Bob");

animal = rhino;
animal = employee; // Error: Animal and Employee are not compatible

这个例子中有AnimalRhino两个类,RhinoAnimal类的子类。
还有一个Employee类,其类型看上去与Animal是相同的。
我们创建了几个这些类的实例,并相互赋值来看看会发生什么。
因为AnimalRhino共享了来自Animal里的私有成员定义private name: string,因此它们是兼容的。
然而Employee却不是这样。当把Employee赋值给Animal的时候,得到一个错误,说它们的类型不兼容。
尽管Employee里也有一个私有成员name,但它明显不是Animal里面定义的那个。

理解protected

protected修饰符与private修饰符的行为很相似,但有一点不同,protected成员在派生类中仍然可以访问。例如:

class Person {
    protected name: string;
    constructor(name: string) { this.name = name; }
}

class Employee extends Person {
    private department: string;

    constructor(name: string, department: string) {
        super(name)
        this.department = department;
    }

    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}

var howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // error

注意,我们不能在Person类外使用name,但是我们仍然可以通过Employee类的实例方法访问,因为Employee是由Person派生出来的。

参数属性

在上面的例子中,我们不得不定义一个私有成员name和一个构造函数参数theName,并且立刻给nametheName赋值。
这种情况经常会遇到。参数属性可以方便地让我们在一个地方定义并初始化一个成员。
下面的例子是对之前Animal类的修改版,使用了参数属性:

class Animal {
    constructor(private name: string) { }
    move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

注意看我们是如何舍弃了theName,仅在构造函数里使用private name: string参数来创建和初始化name成员。
我们把声明和赋值合并至一处。

参数属性通过给构造函数参数添加一个访问限定符来声明。
使用private限定一个参数属性会声明并初始化一个私有成员;对于publicprotected来说也是一样。

存取器

TypeScript支持getters/setters来截取对对象成员的访问。
它能帮助你有效的控制对对象成员的访问。

下面来看如何把一类改写成使用getset
首先是一个没用使用存取器的例子。

class Employee {
    fullName: string;
}

var employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    console.log(employee.fullName);
}

我们可以随意的设置fullName,这是非常方便的,但是这也可能会带来麻烦。

下面这个版本里,我们先检查用户密码是否正确,然后再允许其修改employee。
我们把对fullName的直接访问改成了可以检查密码的set方法。
我们也加了一个get方法,让上面的例子仍然可以工作。

var passcode = "secret passcode";

class Employee {
    private _fullName: string;

    get fullName(): string {
        return this._fullName;
    }

    set fullName(newName: string) {
        if (passcode && passcode == "secret passcode") {
            this._fullName = newName;
        }
        else {
            console.log("Error: Unauthorized update of employee!");
        }
    }
}

var employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    alert(employee.fullName);
}

我们可以修改一下密码,来验证一下存取器是否是工作的。当密码不对时,会提示我们没有权限去修改employee。

注意:若要使用存取器,要求设置编译器输出目标为ECMAScript 5或更高。

静态属性

到目前为止,我们只讨论了类的实例成员,那些仅当类被实例化的时候才会被初始化的属性。
我们也可以创建类的静态成员,这些属性存在于类本身上面而不是类的实例上。
在这个例子里,我们使用static定义origin,因为它是所有网格都会用到的属性。
每个实例想要访问这个属性的时候,都要在origin前面加上类名。
如同在实例属性上使用this.前缀来访问属性一样,这里我们使用Grid.来访问静态属性。

class Grid {
    static origin = {x: 0, y: 0};
    calculateDistanceFromOrigin(point: {x: number; y: number;}) {
        var xDist = (point.x - Grid.origin.x);
        var yDist = (point.y - Grid.origin.y);
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor (public scale: number) { }
}

var grid1 = new Grid(1.0);  // 1x scale
var grid2 = new Grid(5.0);  // 5x scale

console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

高级技巧

构造函数

当你在TypeScript里定义类的时候,实际上同时定义了很多东西。
首先是类的实例的类型。

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

var greeter: Greeter;
greeter = new Greeter("world");
console.log(greeter.greet());

在这里,我们写了var greeter: Greeter,意思是Greeter类实例的类型是Greeter
这对于用过其它面向对象语言的程序员来讲已经是老习惯了。

我们也创建了一个叫做构造函数的值。
这个函数会在我们使用new创建类实例的时候被调用。
下面我们来看看,上面的代码被编译成JavaScript后是什么样子的:

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

var greeter;
greeter = new Greeter("world");
console.log(greeter.greet());

上面的代码里,var Greeter将被赋值为构造函数。
当我们使用new并执行这个函数后,便会得到一个类的实例。
这个构造函数也包含了类的所有静态属性。
换个角度说,我们可以认为类具有实例部分与静态部分这两个部分。

让我们来改写一下这个例子,看看它们之前的区别:

class Greeter {
    static standardGreeting = "Hello, there";
    greeting: string;
    greet() {
        if (this.greeting) {
            return "Hello, " + this.greeting;
        }
        else {
            return Greeter.standardGreeting;
        }
    }
}

var greeter1: Greeter;
greeter1 = new Greeter();
console.log(greeter1.greet());

var greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
var greeter2:Greeter = new greeterMaker();
console.log(greeter2.greet());

这个例子里,greeter1与之前看到的一样。
我们实例化Greeter类,并使用这个对象。
与我们之前看到的一样。

再之后,我们直接使用类。
我们创建了一个叫做greeterMaker的变量。
这个变量保存了这个类或者说保存了类构造函数。
然后我们使用typeof Greeter,意思是取Greeter类的类型,而不是实例的类型。
或者理确切的说,”告诉我Greeter标识符的类型”,也就是构造函数的类型。
这个类型包含了类的所有静态成员和构造函数。
之后,就和前面一样,我们在greeterMaker上使用new,创建Greeter的实例。

把类当做接口使用

如上一节里所讲的,类定义会创建两个东西:类实例的类型和一个构造函数。
因为类可以创建出类型,所以你能够在可以使用接口的地方使用类。

class Point {
    x: number;
    y: number;
}

interface Point3d extends Point {
    z: number;
}

var point3d: Point3d = {x: 1, y: 2, z: 3};

关于术语的一点说明:
必须要注意一点在TypeScript 1.5里,术语名称已经发生了变化。
“Internal modules” 现在叫做 “namespaces”。
“External modules” 现在则简称为 “modules”,为了与ECMAScript 2015里的术语保持一致,(也就是说 module X { 相当于现在推荐的写法 namespace X {)。

命名空间/模块 介绍

这篇文章将概括介绍在TypeScript里使用模块与命名空间组织代码的方法。
我们也会谈及命名空间和模块的高级使用场景,并指出在使用它们的过程中常见的陷井。

查看模块章节了解关于模块的更多信息。
查看命名空间章节了解关于命名空间的更多信息。

使用命名空间

命名空间是在全局命名空间里的一个有名字的JavaScript普通对象。
这令命名空间十分容易使用。
它们可以在多文件中同时使用,并通过--outFile结合在一起。
命名空间是帮助你组织Web应用的好助手,可以把所有依赖都放在页面的

你可能感兴趣的:(typescript)