# 05 TypeScript中的接口

# 接口的作用

在面向对象编程中,接口是一种规范定义,它定义了行为和动作规范,在程序设计中,接口起到一种限制和规范的作用。接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类方法的实现细节,只规定这些类必须提供某些方法,提供这些方法得类就可以满足实际需求。TypeScript中提供的接口类似于Java,但同时增加了更加灵活的接口类型,包括属性、函数、可索引和类等。

# 属性接口

# 对json的约束

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

let myObj = {size:10,label:'Size 10 Object'};
printLabel(myObj);//Size 10 Object

上面例子中,printLabel 函数传入一个对象(labelledObj),并且该对象须有一个string类型的属性字段label

# 对函数参数对象的属性约束

interface关键字

interface FullName {
    firstName: string;
    secondName: string;// 注意需要以分号[;]结尾
}

function printName(name: FullName) {
    //参数name必须有firstName和secondName属性字段
    console.log(`${name.firstName} · ${name.secondName}`);
}
printName({
    age:25, //报错
    firstName: 'Steve',
    secondName: 'Jobs'
});

报错。

类型“{ age: number; firstName: string; secondName: string; }”的参数不能赋给类型“FullName”的参数。

对象文字可以只指定已知属性,并且“age”不在类型“FullName”中。

printName({
    firstName: 'Steve',
    secondName: 'Jobs'
});//Steve · Jobs

正确 以上写法时,参数对象只能包含接口规定的参数以及对应数据类型,多余参数会报错。

var info= {
    age:25,
    firstName: 'Steve',
    secondName: 'Jobs'
}
printName(info);//Steve · Jobs

以上写法也正确 参数对象单独定义,再传入printName函数中,多余的参数不会报错,但接口规定的参数的属性字段数据类型依然必须保持一致。

# 对批量方法约束

printName函数上面两种正确传参的写法,不论哪一种,其实现方法printName的函数体内只能调用接口FullName规定好的属性字段,多余的会报错。比如:

function printName(name: FullName) {
    //参数name必须有firstName和secondName属性字段
    console.log(`${name.firstName} · ${name.secondName} is ${name.age} years old.`);
}

其中在调用参数nameage属性时会报错:

类型“FullName”上不存在属性“age”。

# 可选属性接口

接口定义参数对象属性字段加上问好?,代表可选属性字段。

interface PersonInfo {
    firstName: string;
    secondName: string;
    age?: number;// ? 可选属性字段
}

function printPersonInfo(info: PersonInfo) {
    if (info.age)
        console.log(`${info.firstName} · ${info.secondName} is ${info.age} years old.`);
    else
        console.log(`How old is ${info.firstName} · ${info.secondName} ?`);
}

printPersonInfo({
    age: 25,
    firstName: 'Steve',
    secondName: 'Jobs'
});//Steve · Jobs is 25 years old.

printPersonInfo({
    firstName: 'Steve',
    secondName: 'Jobs'
});//How old is Steve · Jobs ?

# 函数接口

对方法的传入参数以及返回值进行约束。可以批量约束。

// 举例:加密规范接口
interface encrypt {
    (key: string, value: string): string
}

var md5: encrypt = function (key: string, value: string): string {
    // 模拟结果
    return `md5 ${key} ${value}`
}

console.log(md5("asdfgh","Sogrey"));//md5 asdfgh Sogrey

# 可索引接口

# 对数组的约束

回顾ts定义数组定义:

var arr: number[] = [11, 223];
var aar1: Array<string> = ['12', 'ased'];
// 约束数组成员只能是string类型
interface UseArr{
    [index:number]:string
}

var ua:UseArr = ['aaa','sss'];
// var ua:UseArr = ['aaa','sss',1];//不能将类型“number”分配给类型“string”。

console.log(ua[1]);//sss

例子中定义的接口UseArr约束数组成员只能是string类型,当你定义一个UseArr类型的数组,其成员不全是string就会报错,这是UseArr接口约束的。

当然,如果将UseArr修改为:

interface UseArr{
    [index:number]:any
}

any类型即不约束类型。

以上是定义接口对数组的约束,下面看下对对象的约束。

# 对对象的约束

interface UseObject{
    [index:string]:string
}

var uo:UseObject = {
    index:'001',
    name:'Steve Jobs'
};

console.log(uo);//{index: "001", name: "Steve Jobs"}

这次接口UseObject定义中indexstring类型,表明是对象的index属性类型为string类型。

# 类类型接口

对类的约束,与抽象类相似。

interface Animals {
    name: string;
    eat(str: string): void;
}

class Cattle implements Animals {
    name: string;
    constructor(name: string) {
        this.name = name;
    }

    eat() {// 没有传参 str 也没有报错,但eat方法必须实现
        console.log(`${this.name} eat grass.`);
    }
}

var cattle = new Cattle('Beitina');
cattle.eat();//Beitina eat grass.

定义类Cattle必须要有name属性和eat方法,这是接口Animals规定的,但是eat方法没有传参str也没有报错。

# 接口扩展

接口可以继承接口。

interface Animal1 {
    eat():void;
}
interface Person1 extends Animal1{
    work():void;
}

class Adult implements Person1{
    eat(){
        console.log(`要吃饭`);
    }
    work(){
        console.log(`要工作`);
    }
}

var adult = new Adult();
adult.work();//要工作
adult.eat();//要吃饭

以上例子中接口Person1继承自Animal1,类Adult实现接口Person1,则要全部实现接口Person1以及接口Animal1中所有的方法。

再扩展:

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

    coding(code: string) {
        console.log(`在编程:${code}`);
    }
}
class Adult extends Programmer implements Person1 {
    eat() {
        console.log(`要吃饭`);
    }
    work() {
        console.log(`要工作`);
    }
}

var adult = new Adult(`Sogrey`);
adult.work();//要工作
adult.eat();//要吃饭
adult.coding(`TypeScript`);//在编程:TypeScript

这次类Adult继承自父类Programmer,并且要实现接口Person1