Skip to content

TS清单 - 接口

接口

TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。
在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

简单的DEMO

js
interface IName {
    name: string;
}

function sayHello(user: IName) {
    console.log(user.name)
}

let Bob = {name: 'Bob', age: 28}
// 传入的对象满足上面提到的必要条件,就是被允许的。
sayHello(Bob)       // print: Bob
// sayHello({name: 'Bob', age: 28}) // 报错:类型“{ name: string; age: number; }”的参数不能赋给类型“IName”的参数。

可选属性 / 任意属性 / 只读属性

js
interface IUser {
    readonly id: number;
    // 必要属性
    name: string;
    // 可选属性
    age?: number;
    // 任意属性
    [propName: string]: any;
}

function sayHello(user: IUser) {
    console.log(user.name)
}

let Bob = {id: 1, name: 'Bob', age: 28}
sayHello(Bob)                            // print: Bob
sayHello({id: 2, name: 'BoBo', age: 28}) // print: BoBo

// 验证 只读属性
let testUser: IUser = {id: 3, name: 'xiaoli'}
testUser.id = 4     // 报错:无法为“id”赋值,因为它是只读属性。

readonly vs const

最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。
做为变量使用的话用 const,若做为属性则使用readonly。

函数类型

接口能够描述JavaScript中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。

js
interface IFullName {
    (firstName: string, lastName: string): string;
}

let fullName: IFullName;
fullName = function (firstName: string, lastName: string): string {
    return firstName + ' ' + lastName
}
console.log(fullName('Yuan', 'Bob'))    // print: Yuan Bob

可索引的类型

TypeScript支持两种索引签名:字符串和数字。
可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。

js
interface StringArray {
  [index: number]: string;
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

类类型

js
// 定义接口
interface Animal {
    sleep: string;
}
// 类 实现 接口
class Dog implements Animal {
    sleep: string;
    name: string;
    constructor(name: string) {
        this.name = name;
        this.sleep = '躺着睡觉';
    }
}
// 实例化 类
const xiaohuang = new Dog('小黄')
console.log(xiaohuang.name)     // print: 小黄
console.log(xiaohuang.sleep)    // print: 躺着睡觉

继承接口

js
// 定义接口 A
interface Animal {
    sleep: string;
}
// 定义接口 B
interface Person {
    eat: (sth: string) => void;
    name: string;
    age?: number;
}
interface Children extends Animal, Person {
    play: (sth: string) => void;
}

// 接口实现
let xiaohong = <Children>{};
xiaohong = {
    sleep: '玩累了就睡觉',
    name: '小红',
    eat: function (food: string) {
        console.log(this.name + ' eat ' + food)
    },
    play: function(toy: string) {
        console.log(this.name + ' play ' + toy)
    }
}
xiaohong.eat('汉堡包')  // print: 小红 eat 汉堡包
xiaohong.play('玩具熊') // print: 小红 play 玩具熊

混合类型

一个对象可以同时做为函数和对象使用,并带有额外的属性。

js
// DEMO from 中文官网
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;

接口继承类

当接口继承了一个类类型时,它会继承类的成员但不包括其实现。就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。

js
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() { }
}

class Location {

}

在上面的例子里,SelectableControl包含了Control的所有成员,包括私有成员state。 因为 state是私有成员,所以只能够是Control的子类们才能实现SelectableControl接口。 因为只有 Control的子类才能够拥有一个声明于Control的私有成员state,这对私有成员的兼容性是必需的。
在Control类内部,是允许通过SelectableControl的实例来访问私有成员state的。 实际上, SelectableControl接口和拥有select方法的Control类是一样的。 Button和TextBox类是SelectableControl的子类(因为它们都继承自Control并有select方法),但Image和Location类并不是这样的。

参考地址: Interfaces(中文版)