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); // OKTypeScript 的类型检查器只关心对象是否具有接口所要求的属性和类型,而不关心对象是否还有其他属性。这被称为“鸭子类型”(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),那么使用接口是更好的选择。如果你需要定义联合类型、元组或者其他非对象类型,那么应该使用类型别名。
总的来说,接口是定义代码契约和规范的强大工具。