TypeScript 接口:interface 定义与实现
TypeScript 的 interface 是定义对象“形状”的核心工具。它不生成任何运行时代码,仅在编译阶段进行类型检查,确保你使用的对象拥有预期的属性和方法。
1. 基础接口定义
创建一个最简单的接口,只需使用 interface 关键字后跟接口名和花括号:
interface User {
name: string;
age: number;
}
这个 User 接口规定:任何符合该类型的对象必须包含一个名为 name 的字符串属性和一个名为 age 的数字属性。
声明一个变量并指定其类型为 User:
const person: User = {
name: "张三",
age: 28
};
如果遗漏任一属性,或属性类型不符(如 age: "28"),TypeScript 编译器会报错。
2. 可选属性与只读属性
可选属性
在属性名后添加问号 ?,表示该属性可以不存在:
interface Car {
brand: string;
color?: string; // 可选
}
现在你可以这样使用:
const myCar: Car = { brand: "Toyota" }; // 合法,color 可省略
const yourCar: Car = { brand: "Honda", color: "blue" }; // 也合法
只读属性
在属性名前加 readonly,表示该属性在对象创建后不可修改:
interface Point {
readonly x: number;
readonly y: number;
}
尝试修改只读属性会触发错误:
const origin: Point = { x: 0, y: 0 };
// origin.x = 1; // 错误!Cannot assign to 'x' because it is a read-only property.
注意:
readonly是编译时约束,不阻止运行时通过类型断言绕过。但它能有效防止意外修改。
3. 函数类型与方法定义
接口不仅能描述数据结构,还能描述函数签名。
描述函数类型
interface GreetFunction {
(name: string): string;
}
这表示一个接受 string 参数并返回 string 的函数。实现如下:
const sayHello: GreetFunction = (name) => `你好,${name}!`;
```
### 在对象中定义方法
更常见的是在对象接口中直接声明方法:
```typescript
interface Calculator {
add(a: number, b: number): number;
subtract(a: number, b: number): number;
}
```
**创建**一个符合该接口的对象:
```typescript
const calc: Calculator = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
}
};
```
注意:方法定义中参数类型可省略(因接口已声明),但返回类型建议保留以增强可读性。
---
## 4. 实现接口:class 与 implements
使用 `implements` 关键字让类遵循某个接口的契约。
**定义**一个接口:
```typescript
interface Animal {
name: string;
makeSound(): void;
}
```
**创建**一个类并实现该接口:
```typescript
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound() {
console.log("汪汪!");
}
}
```
关键点:
- 类必须**显式声明**所有接口要求的属性(如 `name: string`)。
- 必须**提供**所有接口方法的具体实现。
- 可以添加接口未要求的额外属性或方法。
一个类可以同时实现多个接口:
```typescript
interface Flyable {
fly(): void;
}
class Bird implements Animal, Flyable {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound() {
console.log("啾啾!");
}
fly() {
console.log("飞起来啦!");
}
}
```
---
## 5. 接口继承与合并
### 接口继承
使用 `extends` 让一个接口基于另一个接口扩展:
```typescript
interface Shape {
color: string;
}
interface Circle extends Shape {
radius: number;
}
```
现在 `Circle` 接口包含 `color` 和 `radius` 两个属性。**使用**时:
```typescript
const circle: Circle = {
color: "red",
radius: 5
};
```
支持多重继承:
```typescript
interface Drawable {
draw(): void;
}
interface Resizable {
resize(factor: number): void;
}
interface UIElement extends Drawable, Resizable {
id: string;
}
```
`UIElement` 同时拥有 `draw()`、`resize()` 方法和 `id` 属性。
### 接口自动合并
如果在同一作用域内**多次声明**同名接口,TypeScript 会自动将其成员合并:
```typescript
interface Box {
width: number;
}
interface Box {
height: number;
}
```
最终 `Box` 接口等价于:
```typescript
interface Box {
width: number;
height: number;
}
```
这一特性常用于为第三方库扩展类型定义(如在 `.d.ts` 文件中补充缺失的属性)。
---
## 6. 索引签名与任意属性
当对象的属性名不确定,但值类型固定时,使用索引签名。
### 字符串索引签名
```typescript
interface StringArray {
[index: number]: string;
}
```
这表示用数字索引访问时返回字符串,适用于类数组对象。
更常用的是对象形式的任意属性:
```typescript
interface AnyObject {
[key: string]: any;
}
```
现在可以给 `AnyObject` 添加任意字符串键的属性:
```typescript
const obj: AnyObject = {};
obj.foo = 123;
obj.bar = "hello";
```
> 警告:过度使用 `[key: string]: any` 会削弱类型安全性,应尽量避免。
### 约束任意属性类型
若希望所有额外属性都符合特定类型,可结合已知属性使用:
```typescript
interface Options {
timeout: number;
retries: number;
[key: string]: string | number; // 其他属性只能是 string 或 number
}
```
这样既保留了明确的配置项,又允许灵活扩展。
---
## 7. 与类型别名 type 的区别
TypeScript 还有 `type` 别名,也能定义对象形状。两者主要区别如下:
| 特性 | `interface` | `type` |
| :--- | :---: | :---: |
| 可扩展(合并) | ✅ 自动合并同名接口 | ❌ 不可重复定义 |
| 支持计算属性 | ❌ 有限支持 | ✅ 完全支持(如联合类型、元组) |
| 实现(implements) | ✅ 可被 class 实现 | ✅ 可被 class 实现 |
| 描述原始类型 | ❌ 不能 | ✅ 可以(如 `type ID = string`) |
**优先使用 `interface`**,除非你需要:
- 定义联合类型(如 `type Status = "success" | "error"`)
- 使用元组(如 `type Point = [number, number]`)
- 映射类型或条件类型
---
## 8. 实战示例:API 响应结构定义
假设你调用一个用户信息 API,返回如下结构:
```json
{
"id": 101,
"username": "alice",
"email": "alice@example.com",
"profile": {
"bio": "Frontend developer",
"avatar": "https://example.com/avatar.jpg"
}
}
```
**定义**对应的接口:
```typescript
interface UserProfile {
bio: string;
avatar: string;
}
interface UserResponse {
id: number;
username: string;
email: string;
profile: UserProfile;
}
```
**使用**时配合 `fetch`:
```typescript
async function fetchUser(id: number): Promise<UserResponse> {
const res = await fetch(`/api/users/${id}`);
return res.json();
}
fetchUser(101).then(user => {
console.log(user.profile.bio); // 类型安全,自动补全
});
这种分层接口设计让代码清晰、可维护,并在数据结构变化时快速暴露问题。
定义接口时始终从使用者角度出发,关注“需要什么”,而非“有什么”。通过 interface 明确契约,你的 TypeScript 代码将获得更强的健壮性和可读性。

暂无评论,快来抢沙发吧!