在通常情况下,接口是用来定义一些规范,使用这些接口,就必须实现按照接口中的规范来走。

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

基本用法

const getFullName = (obj:{firstName:string, lastName:string}) => {
    return `${obj.firstName} ${obj.lastName}`;
}
getFullName({
    firstName: 'li',
    lastName: 'si'
});
1
2
3
4
5
6
7

定义接口的关键字是interface。我们现在就来定义一个接口:

interface Person {
    name: string,
    age: number
}

let tom:Person = {
    name: 'lisi',
    age: 22
}
1
2
3
4
5
6
7
8
9

上述例子中,我们定义了一个接口Person,接着定义了一个变量tom,它的类型是Person。这样,我们就约束了tom的形状必须和接口Person一致。接口一般首字母大写。

interface NameInfo {
    firstName:string,
    lastName:string
}
const getFullName = ({firstName, lastName}:NameInfo) => {
    return `${firstName} ${lastName}`;
}
getFullName({
    firstName: 'li',
    lastName: 'si'
});
1
2
3
4
5
6
7
8
9
10
11

属性接口(对json对象的约束)

// ts中自定义方法传入参数,对json进行约束
function sayName(info:{name:string}):string {
    return info.name;
}

console.log(sayName(111)); // 错误写法,传入的参数不是对象
console.log(sayName({age: 12})); // 错误写法,传入的参数对象中没有name属性
console.log(sayName({name: 'lisi'})); // 正确写法
1
2
3
4
5
6
7
8
// 对批量方法传入参数进行约束
// 接口:行为和动作的规范,对批量方法进行约束

// 就是传入对象的约束:属性接口
interface FullName {
    firstName:string; // 约束
    secondName:string;
}
// 约束firstName和secondName是必传参数
// 必须传入对象参数
function sayName(name:FullName) {
    console.log(name.firstName + '---' + name.secondName + '---' + name.age);
}
let obj = {
    firstName: 'li',
    secondName: 'si',
    age: 12
};
// 传入的参数必须包含firstName和secondName
sayName(obj);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

可选属性

interface FullName {
    firstName:string; // 约束
    secondName?:string; // 可选参数
}
// 必须传入对象参数
function sayName(name:FullName) {
    console.log(name.firstName);
}
let obj = {
    firstName: 'li'
};
// 传入的参数必须包含firstName,但是secondName可选
sayName(obj);
1
2
3
4
5
6
7
8
9
10
11
12
13

多余属性检查

interface Vegetable {
    color?:string,
    type:string
}

const getVegetables = ({color, type}:Vegetable) => {
    return `A ${color ? (color + ' ') : ''}${type}`;
}

console.log(getVegetables({
    type: 'tomato',
    color: 'red',
    size: 123 // 报错-多余属性
}));
1
2
3
4
5
6
7
8
9
10
11
12
13
14

绕开多余属性检查

使用类型断言

interface Vegetable {
    color?:string,
    type:string
}

const getVegetables = ({color, type}:Vegetable) => {
    return `A ${color ? (color + ' ') : ''}${type}`;
}

console.log(getVegetables({
    type: 'tomato',
    color: 'red',
    size: 123
} as Vegetable)); // 使用类型断言来绕开多余属性检查
1
2
3
4
5
6
7
8
9
10
11
12
13
14

使用索引签名

interface Vegetable {
    color?:string,
    type:string,
    [prop:string]:any // 索引签名
}

const getVegetables = ({color, type}:Vegetable) => {
    return `A ${color ? (color + ' ') : ''}${type}`;
}

console.log(getVegetables({
    type: 'tomato',
    color: 'red',
    size: 123
}));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

使用类型兼容性

interface Vegetable {
    color?:string,
    type:string
}

const getVegetables = ({color, type}:Vegetable) => {
    return `A ${color ? (color + ' ') : ''}${type}`;
}

const vegetableInfo = {
    type: 'tomato',
    color: 'red',
    size: 123
};

console.log(getVegetables(vegetableInfo));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

只读属性

interface Vegetable {
    color?:string,
    readonly type:string
}

const vegetableObj:Vegetable = {
    type: 'tomato'
}

vegetableObj.type = 'apple'; // 报错
1
2
3
4
5
6
7
8
9
10
// 数组只读
interface ArrInter {
    0: number,
    readonly 1: string
}
const arrInter: ArrInter = [1, 'a'];
arrInter[1] = 'we'; // 报错
1
2
3
4
5
6
7

应用

// 属性接口应用--ajax请求封装
interface Config {
    type:string,
    url:string,
    data?:string, // 可选属性
    dataType:string
}
function ajax(config:Config) {
    let xhr = new XMLHttpRequest();
    xhr.open(config.type, config.url, true);
    xhr.send(config.data);
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200) {
            if (config.dataType === 'json') {
                console.log(JSON.parse(xhr.responseText));
            }
            else {
                console.log(xhr.responseText);
            }
        }
    }
}

ajax({
    type: 'get',
    data: 'name=lisi',
    url: 'http://www.baidu.com',
    dataType: 'json'
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

函数类型接口

函数类型接口:对方法传入的参数和返回值进行约束。

// 函数类型接口:可以实现批量约束
// 加密的函数类型接口
interface encrypt {
    // 接收两个string类型参数key和value,返回值也是string类型
    (key:string, value:string):string;
}
// md5方法必须遵循encrypt接口规范:key、value以及返回值都是string类型
let md5:encrypt = function(key:string, value:string):string {
    // 模拟md5操作
    return key + value;
}
console.log(md5('name', 'lisi'));
1
2
3
4
5
6
7
8
9
10
11
12

可索引接口

可索引接口:对数组或者对象的约束(不常用)。

interface UserArr {
    // 定义了索引是数字,值是字符串的数组接口
    [index:number]:string
}

let arr22:UserArr = ['111', '222'];
1
2
3
4
5
6
interface UserObj {
    // 定义了索引是字符串,值是字符串的对象接口
    [index:string]:string
}

let obj22:UserObj = {name: 'lisi'};
1
2
3
4
5
6

类类型接口

类类型接口:对类的约束,和抽象类有点相似

// 类类型接口
interface Animal {
    name:string; // 属性
    eat(str:string):string; // 方法
}
// 实现接口(必须要有name属性和eat方法)
class Dog implements Animal {
    name:string;
    constructor(name:string) {
        this.name = name;
    }
    eat():string {
        return this.name;
    }
}

let dog2 = new Dog('xiaohuang');
console.log(dog2.eat());

class Cat implements Animal {
    name:string;
    constructor(name:string) {
        this.name = name;
    }
    eat(food:string):string {
        return `${this.name}${food}`;
    }
}

let cat = new Cat('xiaohua');
console.log(cat.eat('黄花鱼')); // xiaohua吃黄花鱼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

接口继承

接口扩展:接口可以继承接口。

interface Animal {
    eat():string; // Animal接口有一个eat方法
}
// Person接口继承Animal接口
interface Person extends Animal {
    work():string;
}

class Woman implements Person {
    public name:string;
    constructor(name:string) {
        this.name = name;
    }
    eat():string {
        return `${this.name}爱吃米饭`;
    }
    work():string {
        return `${this.name}爱工作`;
    }
}

let woman = new Woman('lisi');
console.log(woman.eat());
console.log(woman.work());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

继承类并实现接口

interface Animal {
    eat():string;
}

interface Person extends Animal {
    work():string;
}

class Programmer {
    public name:string;
    constructor(name:string) {
        this.name = name;
    }
    coding():string {
        return `${this.name}爱写代码`;
    }
}

// 继承类并实现接口
class Woman extends Programmer implements Person {
    constructor(name:string) {
        super(name);
    }
    // 实现接口的eat和work方法
    eat():string {
        return `${this.name}爱吃米饭`;
    }
    work():string {
        return `${this.name}爱工作`;
    }
}

let woman = new Woman('lisi');
console.log(woman.coding());
console.log(woman.eat());
console.log(woman.work());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

再来看个例子:

interface Vegetables {
    color: string
}
// Tomato从Vegetables继承了color属性
interface Tomato extends Vegetables {
    size: number
}

interface Carrot {
    length: number
}

const tomato: Tomato = {
    size: 123,
    color: 'red'
}

const carrot: Carrot = {
    length: 123
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

混合类型接口

interface Counter { // 定义一个函数接口,该函数有一个count属性
    (): void,
    count: number
}

const getCounter = (): Counter => {
    const c = () => { c.count++ }
    c.count = 0;
    return c;
}
const counter: Counter = getCounter();
counter();
console.log(counter.count); // 1
counter();
console.log(counter.count); // 2
counter();
console.log(counter.count); // 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17