zoukankan      html  css  js  c++  java
  • 【TypeScript】TypeScript 学习 2——接口

    在 TypeScript 中,接口是用作约束作用的,在编译成 JavaScript 的时候,所有的接口都会被擦除掉,因为 JavaScript 中并没有接口这一概念。

    先看看一个简单的例子:

    function printLabel(labelledObj: { label: string }) {
        console.log(labelledObj.label);
    }
    
    var myObj = { size: 10, label: "Size 10 Object" };
    printLabel(myObj);

    那么在该方法中,labelledObj 的类型就是 {label: string},看上去可能有点复杂,但我们看见看看下面 myObj 的声明就知道,这是声明了一个拥有 size 属性(值为 10)和 label 属性(值为 "Size 10 Object")的对象。所以方法参数 labelledObj 的类型是 {label: string} 即表明参数拥有一个 string 类型的 label 属性。

    但是,这么写的话,这个方法看上去还是有点让人糊涂。那么就可以用接口(interface)来定义这个方法的参数类型。

    interface LabelledValue {
        label: string;
    }
    
    function printLabel(labelledObj: LabelledValue) {
        console.log(labelledObj.label);
    }
    
    var myObj = { size: 10, label: "Size 10 Object" };
    printLabel(myObj);
    • 可选属性

    有些时候,我们并不需要属性一定存在,就可以使用可选属性这一特性来定义。

    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" });

    那么我们就传入了实现一个 SquareConfig 接口的对象入 createSquare 方法。

    既然完全是可有可无的,那么为什么还要定义呢?对比起完全不定义,定义可选属性有两个优点。1、如果存在属性,能约束类型,这是十分关键的;2、能得到语法智能提示,假如误将方法体中 color 写成 collor,那么编译是不通过的。

    • 方法类型

    在 JavaScript 中,方法 function 是一种基本类型。在面向对象思想中,接口的实现是靠类来完成的,而 function 作为一种类型,是不是能够实现接口呢?答案是肯定的。

    在 TypeScript 中,我们可以使用接口来约束方法的签名。

    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) {
      var result = src.search(sub);
      if (result == -1) {
        return false;
      }
      else {
        return true;
      }
    }

    这样也是能够编译通过的。

    • 数组类型

    在上面我们在接口中定义了方法类型,那么,数组类型又应该如何定义呢?很简单。

    interface StringArray {
      [index: number]: string;
    }
    
    var myArray: StringArray;
    myArray = ["Bob", "Fred"];

    那么 myArray 就是一个数组,并且索引器是 number 类型,元素是 string。

    在接口的定义里面,索引器的名字一般为 index(当然也可以改成别的,但一般情况下都是保持名字为 index)。所以改成

    interface StringArray {
      [myIndex: number]: string;
    }
    
    var myArray: StringArray;
    myArray = ["Bob", "Fred"];

    也是 ok 的。

    需要注意的是,索引器的类型只能为 number 或者 string。

    interface Array{
        [index: number]: any;
    }
    
    interface Dictionary{
        [index: string]: any;
    }

    上面两段都是可以编译通过的。

    最后还有一点要注意的是,如果接口已经是数组类型的话,接口中定义的其它属性的类型都必须是该数组的元素类型。例如:

    interface Dictionary {
      [index: string]: string;
      length: number;    // error, the type of 'length' is not a subtype of the indexer
    }

    那么将无法编译通过,需要将 length 改成 string 类型才可以。

    • 使用类实现接口

    一般情况下,我们还是习惯使用一个类,实现需要的接口,而不是像上面直接用接口。

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

    在 TypeScript 中,使用 class 关键字来声明了,这跟 EcmaScript 6 是一样的。

    另外,我们可以使用接口来约束类中定义的方法。

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

    在 TypeScript 中,我们可以为接口定义构造函数。

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

    接下来天真的我们可能会接着这么写:

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

    这是不行的!!!因为构造函数是 static(静态)的,而类仅能够实现接口中的 instance(实例)部分。

    那么这个接口中定义的构造函数岂不是没作用?既然 TypeScript 提供了这项功能,那么肯定不会是没作用的。声明的方法比较特殊:

    interface ClockStatic {
        new (hour: number, minute: number);
    }
    
    class Clock  {
        currentTime: Date;
        constructor(h: number, m: number) { }
    }
    
    var cs: ClockStatic = Clock;
    var newClock = new cs(7, 30);

    正常情况下我们是写 new Clock 的,这里就将 Clock 类指向了 ClockStatic 接口。需要注意的是,newClock 变量的类型是 any。

    • 继承接口

    像类一样,接口也能实现继承,使用的是 extends 关键字。

    interface Shape {
        color: string;
    }
    
    interface Square extends Shape {
        sideLength: number;
    }
    
    var square = <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>{};
    square.color = "blue";
    square.sideLength = 10;
    square.penWidth = 5.0;

    需要注意的是,尽管支持继承多个接口,但是如果继承的接口中,定义的同名属性的类型不同的话,是不能编译通过的。

    interface Shape {
        color: string;
        test: number;
    }
    
    interface PenStroke {
        penWidth: number;
        test: string;
    }
    
    interface Square extends Shape, PenStroke {
        sideLength: number;
    }

    那么这段代码就无法编译通过了,因为 test 属性的类型无法确定。

    • 同时使用上面所述的类型

    如果仅能单一使用某种类型,那么这接口也未免太弱了。但幸运的是,我们的接口很强大。

    interface Counter {
        (start: number): string;
        interval: number;
        reset(): void;
    }
    
    var c: Counter;
    c(10);
    c.reset();
    c.interval = 5.0;

    这样就使用到三种类型了,分别是方法(接口自己是个方法)、属性、方法(定义了方法成员)。

  • 相关阅读:
    PhoneGap打包webApp
    mysql触发器实例说明
    mysql索引总结
    python:生成器
    python:装饰器
    python:局部变量与全局变量
    python:函数
    python:文件操作
    python:集合及其运算
    python:字符串常用函数
  • 原文地址:https://www.cnblogs.com/h82258652/p/4587499.html
Copyright © 2011-2022 走看看