20160112

继续学习TypeScript

继续学习接口

  • 复习接口
1.interface labelledValue{
2. label :string;
3.} // 定义了一个接口
4.
5.function printLabel(labelObj: labelValue){
6. console.log(labelObj.label);
7.} // 参数为接口类型
8.
9.var myObj = {size: 10, label: "hello world"}; // 传入一个外表看起来相似的对象
10.console.log(printLabel(myObj)); // 执行这个接口
  • Function Types
    Interfaces are capable of describing the wide range of shapes that JavaScript objects can take. In addition to describing an object with properties, interfaces are also capable of describing function types.
    To describe a function type with an interface, we give the interface a call signature. This is like a function declaration with only the parameter list and return type given.
    接口能够描述 JavaScript 的对象拥有各种各样的外形。接口除了可以描述带有属性的普通对象之外,还可以描述函数类型。为了让借口描述一个函数类型,需要给接口一个调用签名。这就像是一个函数的声明,只有参数列表和返回值类型。参数列表每一个参数都要有名字和类型。
1.interface SearchFunc{
2. (source: string, subString: string): boolean;
3.} // (参数名: 参数类型): 返回值类型;
4.var mySearch: SearchFunc;
5.mySearch = function(source: string,subString: string){
6. var result=source.search(subString);
7. // search() 方法执行一个查找,看该字符串对象与一个正则表达式是否匹配。
8. if(result===-1){
9. return false;
10. } else {
11. return true;
12. }
13.}

For function types to correctly type-check, the name of the parameters do not need to match. We could have, for example, written the above example like this:
对于函数的类型检查,参数名不一定需要与接口中的参数名完全匹配。

1.interface SearchFunc{
2. (source: string, subString: string): boolean;
3.} // (参数名: 参数类型): 返回值类型;
4.var mySearch: SearchFunc;
5.mySearch = function(source: string,sub: string){
6. var result=source.search(sub);
7. if(result===-1){
8. return false;
9. } else {
10. return true;
11. }
12.}

会对参数类型逐个进行检查,只要对应位置的参数类型匹配就可以,不写参数类型。函数也会根据接口的定义推断出参数类型。

1.interface SearchFunc{
2. (source: string, subString: string): boolean;
3.} // (参数名: 参数类型): 返回值类型;
4.var mySearch: SearchFunc;
5.mySearch = function(src, sub){
6. var result=src.search(sub);
7. if(result===-1){
8. return false;
9. } else {
10. return true;
11. }
12.}
  • Array Types
    Similarly to how we can use interfaces to describe function types, we can also describe array types. Array types have an ‘index’ type that describes the types allowed to index the object, along with the corresponding return type for accessing the index.
    就像是可以用接口描述函数类型一样,我们也可以描述数组类型。数组有一个 index 描述数组的索引类型,还有一个返回值类型表示数组中的元素类型。
1.interface StringArray{    // 接口的首字母一般大写
2. [index: number]: string
3.}
4.var myArray: StringArray;
5.myArray=["Bob", "John"];

支持两种索引类型:string和number。 数组可以同时使用这两种索引类型,但是有一个限制,数字索引返回值的类型必须是字符串索引返回值的类型的子类型。
索引签名能够很好的描述数组和dictionary模式,它们也要求所有属性要与返回值类型相匹配。 因为字符串索引表明obj.property和obj[“property”]两种形式都可以。 下面的例子里,name的类型与字符串索引类型不匹配,所以类型检查器给出一个错误提示:

1.interface Dictionary {
2. [index: string]: string;
3. length: number; // error, the type of 'length' is not a subtype of the indexer
4.}
  • Class Types
    Implementing an interface
    One of the most common uses of interfaces in languages like C# and Java, that of explicitly enforcing that a class meets a particular contract, is also possible in TypeScript.
    就像是 C# 和 Java,接口的一大作用是强制一个类去符合某种契约。
1.interface ClockInterface{
2. currentTime: Date;
3.}
4.class Clock implements ClockInterface{
5. currentTime: Date;
6. constructor(h: number,m: number);
7.}

You can also describe methods in an interface that are implemented in the class, as we do with ‘setTime’ in the below example:
你也可以在接口中描述一个方法,在类里实现它,就像下面的 setTime 方法一样。

1.interface ClockInterface{
2. currentTime: Date;
3. setTime(d: Date);
4.}
5.class Clock implements ClockInterface{
6. currentTime: Date;
7. setTime(d: Date){
8. this.currentTime = d;
9. }
10. constructor(h: number,m: number);
11.}

Interfaces describe the public side of the class, rather than both the public and private side. This prohibits you from using them to check that a class also has particular types for the private side of the class instance.
接口描述了类的公用部分,而不是公用部分和私有部分。它不会帮你检查类是否有某些私有成员。

  • Difference between static/instance side of class 1(待完善)???
    When working with classes and interfaces, it helps to keep in mind that a class has two types: the type of the static side and the type of the instance side. You may notice that if you create an interface with a construct signature and try to create a class that implements this interface you get an error:
    当你去操作类和接口时,你要清楚类是有两个类型的:静态类型和实例类型。你会注意到,当你用构造器签名来定义一个接口并试图创建另一个类去实现这个接口是会有错误。
1.interface ClockInterface{
2. new(hour: number,minute: number);
3.}
4.
5.class Clock implements ClockInterface {
6. currentTime: Date;
7. constructor(h: number,m: number){}
8.}

This is because when a class implements an interface, only the instance side of the class is checked. Since the constructor sits in the static side, it is not included in this check.
Instead, you would need to work with the ‘static’ side of the class directly. In this example, we work with the class directly:
这里因为当一个类实现了一个接口时,只对其实例部分进行类型检查。 constructor存在于类的静态部分,所以不在检查的范围内。
Instead, you would need to work with the ‘static’ side of the class directly. In this example, we work with the class directly:
因此,你需要去直接操作类中的静态部分。在这个例子里,我们直接操作类。

1.interface ClockStatic{
2. new(hour: number,minute: number);
3.} // 用构造器签名声明了一个接口
4.class Clock { // 定义了一个类,并没有用类去实现接口
5. currentTime: Date; // 类的属性 currentTime 是 Date 类型
6. constructor(h: number. m:number){ } // constructor 在类的静态部分
7.}
8.var cs: ClockStatic = Clock; // 定义一个变量,是接口类型,给变量赋值为这个类,他们的结构相似
9.var newClock = new cs(7, 30); // 定义一个变量,值为这个类
  • Extending Interfaces
    Like classes, interfaces can extend each other. This handles the task of copying the members of one interface into another, allowing you more freedom in how you separate your interfaces into reusable components.
    和类一样,接口也可以相互扩展,这个功能让我们可以将成员从一个接口复制到另一个接口里,可以灵活地将分割到可重用的模块里。
1.interface Shape{
2. color: string;
3.}
4.interface Square extends Shape{
5. sideLength: string;
6.} //用这个接口扩展 Shape 接口
7.var square = <Square>{}; // 这是什么??????
8.square.color = "blue";
9.square.sideLength = 10;

An interface can extend multiple interfaces, creating a combination of all of the interfaces.
一个接口可以扩展多个接口,创建一个所有接口的组合。

1.interface Shape{
2. color: String;
3.}
4.interface PenStroke{
5. penWidth: number;
6.}
7.interface Square extends Shape, PenStroke {
8. sideLength: number;
9.}
10.var square=<Square>{};
11.square.color= "blue";
12.square.sidelength= 10;
13.square.penWidth= 5.0;
  • Hybrid Types(混合类型)???
    As we mentioned earlier, interfaces can describe the rich types present in real world JavaScript. Because of JavaScript’s dynamic and flexible nature, you may occasionally encounter an object that works as a combination of some of the types described above.
    One such example is an object that acts as both a function and an object, with additional properties:
    一个对象可以同时有上面提到的多种类型。一个对象可以同时作为函数和对象使用。
1.interface Counter {
2. (start: number): string; // ???
3. interval: number;
4. reset(): void;
5.}
6.var c: Counter;
7.c(10);
8.c.reset();
9.c.interval= 5.0;

当使用第三方库的时候,可能需要向上面一样完整的定义类型。

Classes

Traditional JavaScript focuses on functions and prototype-based inheritance as the basic means of building up reusable components, but this may feel a bit awkward to programmers more comfortable with an object-oriented approach, where classes inherit functionality and objects are built from these classes. Starting with ECMAScript 6, the next version of JavaScript, JavaScript programmers will be able to build their applications using this object-oriented class-based approach. In TypeScript, we allow developers to use these techniques now, and compile them down to JavaScript that works across all major browsers and platforms, without having to wait for the next version of JavaScript.
传统的 JavaScript 使用函数和基于原型的继承来写可重用的组件,但是这对于习惯面向对象的程序员有些棘手,因为它是基于类的继承并且类是由对象创建出来的。从 ECMAScript 6,我们允许 JavaScript 程序员写这种基于类的面向对象方法。

  • Classes
    Let’s take a look at a simple class-based example:
1.class Greeter {
2. greeting: string;
3. constructor(message: string){
4. this.greeting = message;
5. } // constructor ???
6. greet(){
7. return "Hello" + this.greeting;
8. }
9.}
10.var greeter = new Greeter("world");

The syntax should look very familiar if you’ve used C# or Java before. We declare a new class ‘Greeter’. This class has three members, a property called ‘greeting’, a constructor, and a method ‘greet’.
You’ll notice that in the class when we refer to one of the members of the class we prepend ‘this.’. This denotes that it’s a member access.
In the last line we construct an instance of the Greeter class using ‘new’. This calls into the constructor we defined earlier, creating a new object with the Greeter shape, and running the constructor to initialize it.
如果你使用过 C# 或者 Java ,应该对这种语法非常熟悉。
我们声明一个 Greeter类,类中有3个成员,一个叫 greeting 的属性,一个构造函数,一个叫 Greeter的方法。
我们在引用其中一个类成员时用了 this ,表示我们访问的是类的成员。
最后一行,我们用 new 构造了一个 Greeter 的实例,它会调用之前的构造函数,创建一个 Greeter 类型的新对象, 并执行构造函数初始化它。

  • Inheritance(继承)
    In TypeScript, we can use common object-oriented patterns. Of course, one of the most fundamental patterns in class-based programming is being able to extend existing classes to create new ones using inheritance.
    在 TypeScript 中,我们可以使用常用的面向对象模式,当然基于类的程序设计最基本的模式是允许用继承来扩展一个类。
1.class Animal {
2. name: string;
3. constructor(theName: string){ this.name = theName };
4. move(meters: number = 0){
5. alert(this.name + "moved" + meters + "m");
6. }
7.}
8.class Snake extends Animal {
9. constructor(name: string){ super(name); } // 派生类的构造函数,必须调用基类的?
10. move(meters = 5) { // 派生类重写 move 函数
11. alert("Slithering...");
12. super.move(meters); // 调用基类的 move 函数
13. }
14.}
15.class Horse extends Animal {
16. constructor(name: string){ super(name) }
17. move(meters = 45){
18. alert("Galloping...");
19. super.move(meters);
20. }
21.}
22.var sam = new Snake("Sammy the Python");
23.var tom : Animal = new Horse("Tommy the Palomino");
24.
25.sam.move();
26.// Slithering...
27.// Sammy the Python moved 5m.
28.tom.move(34);
29.// Galloping...
30.// Tommy the Palomino moved 34m.

这个例子可以看到 TypeScript 的一些特征,和其他语言类似,我们使用 extends 来创建子类,Horse 和 Shake 是 Animal 的子类,并可以访问其属性和方法。
The example also shows off being able to override methods in the base class with methods that are specialized for the subclass. Here both ‘Snake’ and ‘Horse’ create a ‘move’ method that overrides the ‘move’ from ‘Animal’, giving it functionality specific to each class.
这个例子也写了子类如何重写父类的方法,这里子类都重写了父类的 move 方法,使 move 方法根据不同的类有不同的功能。注意,即使 Horse 被声明为 Animal 类型,但是它的值是 Horse,tom.move(34) 也会调用 Horse 中的重写方法。
包含 constructor 函数的派生类必须调用 super,它会执行基类的构造方法。

  • Private/Public modifiers(公有/私有修饰符)
    1. Public by default
      You may have noticed in the above examples we haven’t had to use the word ‘public’ to make any of the members of the class visible. Languages like C# require that each member be explicitly labelled ‘public’ to be visible. In TypeScript, each member is public by default.
      You may still mark members a private, so you control what is publicly visible outside of your class. We could have written the ‘Animal’ class from the previous section like so:
      默认是 public
      你可以看到,我们没有用任何 public 修饰符,在像 C# 这样的语言里,public 修饰符需要时可见的,但是在 TypeScript 中, public 是默认的。
      你也可以明确的将一个成员标记为 public。我们可以用下面的方式重写 Animal 类。
1.class Animal{
2. public name: string;
3. public constructor(thisName: string){ this.name = thisName; }
4. move(meters: number){
5. alert(this.name+ " moved "+ meters + "m.");
6. }
7.}
  1. Understanding private
    当成员被标记为 private 时,不能在声明它的类外部被访问
    TypeScript is a structural type system. When we compare two different types, regardless of where they came from, if the types of each member are compatible, then we say the types themselves are compatible.
    When comparing types that have ‘private’ members, we treat these differently. For two types to be considered compatible, if one of them has a private member, then the other must have a private member that originated in the same declaration.
    Let’s look at an example to better see how this plays out in practice:
    TypeScript 是结构化类型系统。当我们比较两个类型时,并不在意它们从哪儿来,如果所有成员的类型都是兼容的,那么我们就说他们的类型是兼容的。
    当比较有 private 类型的时候,我们用不同的方式对待。如果一个类型中包含 private 成员,那么只有另一个类型中也包含 private 类型的成员并且它们来自同一处声明时,我们才认为这两个类型是兼容的。
1.class Animal{
2. private name: string;
3. constructor(theName: string){this.name = theName};
4.}
5.class Rhino extends Animal{
6. constructor(){ super("Rhino"); }
7.}
8.class Employee {
9. private name: string;
10. constructor(theName: string){this.name = theName};
11.}
12.var animal = new Animal("Goat");
13.var rhino = new Rhino();
14.var employee = new Employee("Bob");
15.
16.animal = rhino;
17.animal = employee; // error: Animal and Employee are not compatible

In this example, we have an ‘Animal’ and a ‘Rhino’, with ‘Rhino’ being a subclass of ‘Animal’. We also have a new class ‘Employee’ that looks identical to ‘Animal’ in terms of shape. We create some instances of these classes and then try to assign them to each other to see what will happen. Because ‘Animal’ and ‘Rhino’ share the private side of their shape from the same declaration of ‘private name: string’ in ‘Animal’, they are compatible. However, this is not the case for ‘Employee’. When we try to assign from an ‘Employee’ to ‘Animal’ we get an error that these types are not compatible. Even though ‘Employee’ also has a private member called ‘name’, it is not the same one as the one created in ‘Animal’.

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

Employee 中的 name 和 Animal 中的不是来自同一处声明,即使它们看起来一样。

  1. Parameter properties(参数属性)
    The keywords ‘public’ and ‘private’ also give you a shorthand for creating and initializing members of your class, by creating parameter properties. The properties let you can create and initialize a member in one step. Here’s a further revision of the previous example. Notice how we drop ‘theName’ altogether and just use the shortened ‘private name: string’ parameter on the constructor to create and initialize the ‘name’ member.
    这个关键字 public 和 private 用来在类中声明和初始化会有缺点,通过创建参数属性,可以在一个步骤创建并初始化成员。
1.class Animal{
2. constuctor(private name: string){}
3. move(meters: number){
4. alert(this.name + "moved" + meters + "m.");
5. }
6.}

Using ‘private’ in this way creates and initializes a private member, and similarly for ‘public’.
用 private 去声明并初始化一个私有成员,并且和 public 类似。

  1. Accessors(存取器)
    TypeScript supports getters/setters as a way of intercepting accesses to a member of an object. This gives you a way of having finer-grained control over how a member is accessed on each object.
    Let’s convert a simple class to use ‘get’ and ‘set’. First, let’s start with an example without getters and setters.
    TypeScript 支持用 getter / setter 来截取对对象成员的访问,它能帮助你有效控制对对象成员的访问。看看把一个类改为用 get 和 set 的。
1.class Employee{
2. fullName: string;
3.}
4.var employee = new Employee();
5.employee.fullName = "Bob Smith";
6.if(employee.fullName){
7. alert(employee.fullName);
8.}

While allowing people to randomly set fullName directly is pretty handy, this might get us in trouble if we people can change names on a whim.
我们可以随意的设置 fullname ,这是非常方便的,也有可能带来麻烦。

In this version, we check to make sure the user has a secret passcode available before we allow them to modify the employee. We do this by replacing the direct access to fullName with a ‘set’ that will check the passcode. We add a corresponding ‘get’ to allow the previous example to continue to work seamlessly.
在这个例子里,在允许修改 employee 之前,我们先检查密码是否正确。我们把 fullname 的直接访问变成了检查密码的 set 方法。我们也加了一个 get 方法,上述例子仍然可以工作。