TypeScript interface
详解
interface
是 TypeScript 中用来定义对象结构、函数类型、类的类型以及其他复杂数据类型的核心机制之一。它提供了一种灵活且强大的方式来描述数据的形状和结构。了解 interface
的概念、用法及相关的高级特性,可以帮助开发者在项目中更好地利用 TypeScript 的类型系统,提升代码的可维护性、可读性和类型安全性。
1. 基础概念
interface
用来定义对象的形状。通过 interface
,你可以声明一个对象包含哪些属性、属性的类型以及它们是否是可选的。也可以用来定义函数类型、类实现的结构等。
基本语法:
interface InterfaceName {
propertyName: type;
}
示例:
interface Person {
name: string;
age: number;
}
const person: Person = {
name: 'John',
age: 30,
};
2. 可选属性
interface
中的属性默认是必需的,但你可以通过在属性名后面加上 ?
来标记该属性为可选属性。
示例:
interface Person {
name: string;
age?: number; // 可选属性
}
const person1: Person = { name: 'Alice' }; // 没有 age 也合法
const person2: Person = { name: 'Bob', age: 25 }; // 有 age 也是合法
3. 只读属性
你可以使用 readonly
关键字来标记某个属性为只读,确保该属性的值在对象创建后不能被修改。
示例:
interface Point {
readonly x: number;
readonly y: number;
}
const point: Point = { x: 10, y: 20 };
// point.x = 30; // 错误:Cannot assign to 'x' because it is a read-only property
4. 函数类型
interface
还可以用来定义函数类型,即规定函数的参数和返回值的类型。
示例:
interface Sum {
(a: number, b: number): number;
}
const add: Sum = (a, b) => a + b;
5. 可索引类型
interface
还可以用于描述数组或对象的索引签名。当你不知道属性名时,可以使用可索引签名来定义索引类型。
示例:
interface StringArray {
[index: number]: string; // 数组的索引是数字,值是字符串
}
let arr: StringArray = ['apple', 'banana'];
console.log(arr[0]); // 'apple'
还可以定义对象的索引签名:
interface Dictionary {
[key: string]: string;
}
const dict: Dictionary = {
'name': 'Alice',
'age': '25',
};
6. 类实现接口
interface
可以用来定义类的结构,类必须实现接口定义的所有属性和方法。类通过 implements
关键字来实现接口。
示例:
interface Shape {
area: number;
calculateArea(): number;
}
class Circle implements Shape {
radius: number;
area: number = 0;
constructor(radius: number) {
this.radius = radius;
}
calculateArea(): number {
this.area = Math.PI * this.radius ** 2;
return this.area;
}
}
const circle = new Circle(5);
console.log(circle.calculateArea()); // 输出圆的面积
7. 接口继承
interface
可以通过 extends
关键字继承其他接口,这样可以在一个接口中重用其他接口的属性。
示例:
interface Shape {
area: number;
calculateArea(): number;
}
interface ColoredShape extends Shape {
color: string;
}
class ColoredCircle implements ColoredShape {
radius: number;
area: number = 0;
color: string;
constructor(radius: number, color: string) {
this.radius = radius;
this.color = color;
}
calculateArea(): number {
this.area = Math.PI * this.radius ** 2;
return this.area;
}
}
const coloredCircle = new ColoredCircle(5, 'red');
console.log(coloredCircle.color); // 'red'
console.log(coloredCircle.calculateArea()); // 圆的面积
8. 接口与类型别名的区别
虽然 interface
和 type
都可以用来定义对象、函数或其他类型的结构,但两者有一些细微的区别。
- 接口(interface) 主要用来描述对象、类的结构。
- 类型别名(type) 可以用于更广泛的用途,除了可以定义对象的结构外,还可以用于联合类型、交叉类型等。
示例:接口和类型别名的对比
// 接口定义
interface Person {
name: string;
age: number;
}
// 类型别名定义
type PersonType = {
name: string;
age: number;
};
const person1: Person = { name: 'Alice', age: 30 };
const person2: PersonType = { name: 'Bob', age: 25 };
9. 动态接口(索引签名)
如果你不确定一个对象会有多少属性,或者这些属性的名称会随着时间变化,可以使用索引签名。
示例:
interface Car {
brand: string;
[key: string]: string; // 可以有任意多个额外的字符串属性
}
const car: Car = {
brand: 'Toyota',
color: 'red',
model: 'Corolla',
};
10. 接口的合并
interface
具有声明合并的能力。也就是说,如果同一个 interface
被多次声明,TypeScript 会自动将它们合并为一个接口。这对于扩展现有的接口非常有用。
示例:
interface Person {
name: string;
}
interface Person {
age: number;
}
const person: Person = { name: 'Alice', age: 30 }; // 合并后的 Person 接口
11. 高级用法
- 条件类型:结合条件类型和接口,可以创建更灵活的类型系统。
- 映射类型:可以通过映射类型来构建新类型,如将接口的属性变为可选、只读等。
- 泛型接口:接口还可以与泛型一起使用,使得接口能够处理不同类型的数据。
示例:泛型接口:
interface Box<T> {
value: T;
}
const box: Box<number> = { value: 123 };
const stringBox: Box<string> = { value: 'Hello' };
总结
interface
是 TypeScript 中一个非常重要的工具,帮助开发者定义结构化的数据类型。它的应用非常广泛,可以用于:
- 定义对象的形状;
- 用于函数的类型定义;
- 类的实现;
- 继承和扩展现有类型。
通过 interface
,你可以精确地描述数据的结构、功能和约束,确保在代码执行时符合预期的类型,从而提高代码的可维护性和类型安全。