接口 Interface

313 阅读5分钟

接口的作用是对这些类型命名和为你的代码或第三方代码定义契约。

  1. 介绍
// 如果没有接口 如何去校验参数是否含某种属性,且类型为符合的类型

// 例子1  函数接收一个对象参数,对象里必须包含一个label属性,且这个属性为string类型
function printLabel(obj: {label: string}){
    console.log(obj.label);
}

let obj = { size: 10, label: 'fdasf'};
printLabel(obj);



//例子2  利用接口方法 替换掉上面的方式
 // 1. 先定义一个interface接口 首字母大写
  interface LabelObj {
      label: string;   // 代表被校验的对象,必须要包含label属性,且类型为string类型
  }

  function printLabel(obj: LabelObj){
      console.log(obj.label);
  }

  let obj = { size: 10, label: 'fdasf'};
  printLabel(obj);


# 类型检查器,不会检查属性的顺序,只要属性存在,且类型符合要求,那么就是正确的。
  1. 可选的属性 ?

    // 在一些要求里面,对于属性,有时候,不一定都是必须的,可以存在,也可以不存在,这个就是属性的可选
    
    interface SquareVal {
        label: string;
        age?: number;  // 属性后面带 ? 就说明这个属性是可选属性,可存在,也可不存在,存在就校验类型,不存在也没关系
    }
    
  2. 只读属性 readonly 只读数组 ReadonlyArray

    // 在创建的时候,可以需求值,别的时候都不可以了
    interface Point {
        readonly x: number; // x 是只读属性
        readonly x: string; // y 也是只读属性
    }
    
    let p1: Point = { x: 10, y: 'fsef'};
    
    p1.x = 20  // Error 会直接报错,因为是只读属性
    
    
    
    // 只读数组, 将所有可变的方法去掉了,确保数组不能被更改
    let a: ReadonlyArray<number> = [1,2,3];
    a.length = 100// Error
    a.push(22); // Error
    a[4] = 12312; // Error
    
    
    // 如何区分readonly 和 const
    最简单的判断就是 看要把它作为一个变量使用还是作为一个属性
    
    
  3. 额外的属性检查 [propName: string]: any

    // 当你在使用属性的时候,我们设置的对象,存在额外的属性,不在接口的设置内容中,这个时候接口会抛出错误
    
    interface SqureConfig {
        color?: string;
        width?: number;
    }
    
    function prointVal(config: SqureConfig): {color: string, area: number} {
        // ...
    }
    
    // 此时,colour 是不存在于接口SqureConfig里面的, 所以这个时候回抛出错误
    let mySqure = prointVal({colour: 'red', width: 1111});
    
    
    
    // 那么 如何绕开上面的检查呢 
    //方法一 就是利用断言的方式,跳过代码的编译
    
    let mySqure = prointVal({colour: 'red', width: 1111} as SqureConfig);
    
    
    // 方法二 允许接口存在额外属性  数量不进行限制
    
    interface SqureConfig {
        color?: string;
        width?: number;
        [propName:string]: any;   // 属性名为字符串那类型,后面的值,可以是任意类型
    }
    
  4. 函数类型

    // 接口除了带有属性的普通对象外,接口也可以描述函数类型
    
    //1函数的接口的定义包括两部分,一部分是接收参数的描述,一部分是函数return 值得定义
    
    interface FunConfig {
        (source: string, subString: string): boolean;
    }
    
    let mySearch: FunConfig;
    mySearch = function (source: string, subString: string) {
         return false;
    }
    
    //2. 参数名不需要与接口里定义的名字相匹配  函数的参数,会逐个的进行检查,对应位置上的参数的类型是否是符合的
    
    let mySearch: FunConfig;
    mySearch = function (src: string, sub: string){
        return false;
    }
    
  5. 可索引的类型

    // 可以通过给接口配置索引,然后去获取数组或者对象里面的值
    interface SuoYin {
        [index: number]: string;  // 这个是数字类型的索引,它接收的返回值必须是一个字符串
    }
    
    let myArray: SuoYin;
    myArray = ['a', 'b'];
    let myStr: string = myArray[0];
    
    
    // 设置索引之后,在接口内部设置的值,必须与索引的返回值是一样的,不然会报错
    interface Directroy {
        [index: string]: number;
        length: number;
        name: string;  // 这里name的值与,索引需要的返回值不一样,所以这里会进行报错
    }
    
    // 当然,也可以将索引设置为只读的模式,然后索引就不能进行修改了
    interface Directroy {
       readonly [index: string]: number;
    }
    
  6. 类 类型

    //1. 实现接口
    // 类的实现 也可以用接口去判断其内部是否有符合的属性  需要用到implements 来 实现接口
    
    // 这里只能描述类的公共部分,私有部分是描述不了的  constructor 里是静态的 私有的
    interface ClockInterface {
        currentTime: Date;
        setTime(d: Date);
        tick();
    }
    
    class Clock implements ClockInterface {
        currentTime = Date; 
        setTime(d: Date){
            this.currentTime = d;
        }
        tick();
        contructor(h: numgber, m: number){ }; // contructor 是没有办法进行进行接口校验的
    }
    
    
    //2. 如何对 类的静态部分 contructor 进行接口检查呢  ???? 
    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");
        }
    }
    
    let digital = createClock(DigitalClock, 12, 17);
    let analog = createClock(AnalogClock, 7, 32);
    
  7. 继承接口

    //1. 接口之间也是可以进行相互继承的, 其实类似于合并,将另外一个接口的定义属性,放到自己的接口内
    // 单个接口继承
     interface Shape {
         color: sting;
     }
     
     interface Squre extends Shapt {
         sideLenght: number;
     }
    
     let square = <Squre>{};
     square.color = 'red';
     square.sideLength = 10;
    
    
    // 多接口继承
    
     interface PenStorke {
         penState: boolean;
     }
    
    
     interface Squre extends Shape, PenStorke {
         sideLength: number;
     }
    
     let square = <Square>{};
     square.color = "blue";
     square.sideLength = 10;
     square.penState = false;
    
  8. 混合类型

    // 一个对象可以同时做为函数和对象使用,并带有额外的属性。
    interface Counter {
        (start: number): string;
        interval: number;
        reset(): void;
    }
    
    function getCounter(): Counter {
        let counter = <Counter>function (start: number) { };
        counter.interval = 123;
        counter.reset = function () { };
        return counter;
    }
    
    let c = getCounter();
    c(10);
    c.reset();
    c.interval = 5.0;
    
  9. 接口继承类

    //当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。 
    
    // 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时。这个接口类型只能被这个类或其子类所实现(implement)
    
    // 意思就是说,如果一个接口继承了一个类(简称类1),类1有私有属性(private), 或者受保护的属性(protected)时, 如果有一个类(类2)想直接去实现这个接口,是不行的,类2 得先去继承 类1,才可以
    
    // 因为私有属性,不能再声明它的类的外部访问, 受保护的属性,可以在本身类和子类中访问
    
    class Control {
        private state: any;
    }
    
    interface SelectableControl extends Control {
        select(): void;
    }
    
    class Button extends Control implements SelectableControl {
        select() { }
    }
    
    class TextBox extends Control {
        select() { }
    }
    
    // 错误:“Image”类型缺少“state”属性。
    class Image implements SelectableControl {
        select() { }
    }