Typescript - 4. 接口(Interfaces)

164 阅读2分钟

在 TypeScript 中,接口(Interfaces)用于定义对象的类型。它们可以描述对象的形状、类的行为契约,并允许在代码中强制执行特定的结构和类型。接口在 TypeScript 类型系统中扮演了非常重要的角色,提供了灵活的方式来定义数据和方法的类型。

定义接口

接口的基本语法如下:

interface Person {
    name: string;
    age: number;
}

let john: Person = {
    name: "John",
    age: 30
};

// 下面的赋值会导致错误,因为对象不符合接口的定义
// let jane: Person = {
//     name: "Jane"
//     // Error: Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.
// };

可选属性

接口中的属性可以是可选的,通过在属性名后面加上 ? 标记来实现。

interface Person {
    name: string;
    age?: number;
}

let jane: Person = {
    name: "Jane"
}; // OK,因为 `age` 是可选的

只读属性

接口中的属性还可以是只读的,通过使用 readonly 修饰符来指定。这意味着这些属性一旦被赋值就不能再修改。

interface Point {
    readonly x: number;
    readonly y: number;
}

let p1: Point = { x: 10, y: 20 };
// p1.x = 5; // Error: Cannot assign to 'x' because it is a read-only property.

函数类型

接口也可以用来描述函数类型。定义一个具有调用签名的接口:

interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string): boolean {
    return source.indexOf(subString) !== -1;
};

类类型

接口可以用来强制类遵循某种结构。在接口中定义类应有的方法和属性,然后使用 implements 关键字使类实现该接口:

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date): void;
}

class Clock implements ClockInterface {
    currentTime: Date;

    constructor(h: number, m: number) {
        this.currentTime = new Date();
    }

    setTime(d: Date) {
        this.currentTime = d;
    }
}

扩展接口

接口可以通过 extends 关键字进行扩展,这样可以从现有接口里复制成员,创建新接口。

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square: Square = {
    color: "blue",
    sideLength: 10,
    penWidth: 5.0
};

混合类型

接口不仅可以描述对象的形状,还可以描述那些既有属性又有方法的对象。例如,一个对象可以同时作为函数和拥有一些属性:

interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}

function getCounter(): Counter {
    let counter = (function (start: number) {}) as Counter;
    counter.interval = 123;
    counter.reset = function () {};
    return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

接口继承类

接口也可以继承类类型,这意味着接口不仅会继承类的成员,还会继承其私有和受保护的成员:

class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() {}
}

class TextBox extends Control {
    select() {}
}

// Error: Class 'Image' incorrectly implements interface 'SelectableControl'.
// Types have separate declarations of a private property 'state'.
// class Image implements SelectableControl {
//     private state: any;
//     select() {}
// }

在这个例子中,SelectableControl 继承了 Control,因此只有Control 的子类才能实现 SelectableControl 接口。

通过接口,TypeScript 提供了一种强大的方式来定义和约束对象、函数和类的类型,确保代码的一致性和可靠性。