Skip to content

TypeScript 接口

接口(Interface)是 TypeScript 的核心原则之一,它用于定义一个对象的“形状”或“契约”。接口只描述了对象应该包含哪些属性和方法,而不关心其具体的实现。这在团队协作和代码规范方面非常有用。

什么是接口?

接口是一种定义对象结构的方式。你可以用它来指定对象必须有哪些属性,以及这些属性的类型。

基本示例

typescript
// 定义一个接口
interface LabeledValue {
    label: string;
}

function printLabel(labeledObj: LabeledValue) {
    console.log(labeledObj.label);
}

let myObj = { size: 10, label: "Size 10 Object" };

// myObj 拥有一个 string 类型的 label 属性,所以它符合 LabeledValue 接口
printLabel(myObj); // OK

TypeScript 的类型检查器只关心对象是否具有接口所要求的属性和类型,而不关心对象是否还有其他属性。这被称为“鸭子类型”(Duck Typing)。

接口的特性

1. 可选属性

接口里的属性不全都是必需的。你可以在属性名后添加 ? 来标记一个属性为可选。

typescript
interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
    let newSquare = { color: "white", area: 100 };
    if (config.color) {
        newSquare.color = config.color;
    }
    if (config.width) {
        newSquare.area = config.width * config.width;
    }
    return newSquare;
}

let mySquare1 = createSquare({ color: "black" });
let mySquare2 = createSquare({ width: 20 });

2. 只读属性

一些属性只能在对象刚刚创建的时候修改其值。你可以在属性名前用 readonly 来指定只读属性。

typescript
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.

3. 函数类型

接口也可以描述函数类型。为此,接口需要包含一个调用签名,它就像是一个只有参数列表和返回值类型的函数定义。

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

let mySearch: SearchFunc;
mySearch = function(src, sub) {
    let result = src.search(sub);
    return result > -1;
}

4. 可索引类型

接口可以描述那些能够被“索引”的类型,比如数组和对象。

typescript
// 描述一个字符串数组
interface StringArray {
    [index: number]: string;
}

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

// 描述一个字典对象
interface NumberDictionary {
    [index: string]: number;
    length: number; // 可以包含其他属性
}

类实现接口

接口一个常见的用途是强制一个类去满足特定的契约。类可以使用 implements 关键字来实现一个或多个接口。

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

class Clock implements ClockInterface {
    currentTime: Date = new Date();
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}

接口继承

和类一样,接口也可以相互继承。这允许你将一个接口的成员复制到另一个接口中,从而创建出更灵活的、可重用的组件。

typescript
interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

// Square 接口继承了 Shape 和 PenStroke
interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = {} as Square;
square.color = "blue";
square.penWidth = 5.0;
square.sideLength = 10;

接口 vs. 类型别名 (Type Aliases)

接口和类型别名 (type) 有时候可以互换使用,但它们之间也存在一些关键区别:

  • 扩展性: 接口可以被扩展(extends),也可以被同名合并(声明合并)。类型别名不能被扩展或合并,但可以通过交叉类型(&)来模拟扩展。
  • 用途: 如果你是在定义一个对象的“形状”,或者希望它能被类实现(implements),那么使用接口是更好的选择。如果你需要定义联合类型、元组或者其他非对象类型,那么应该使用类型别名。

总的来说,接口是定义代码契约和规范的强大工具。

本站内容仅供学习和研究使用。