Appearance
TypeScript 接口 Interface
接口是 TypeScript 中非常重要的概念,它定义了对象的结构和类型,是 TypeScript 类型系统的核心组成部分。本章节将详细介绍 TypeScript 接口的使用。
什么是接口?
接口(Interface)是 TypeScript 中用于定义对象结构的一种方式,它描述了对象应该具有哪些属性和方法,以及这些属性和方法的类型。
接口只定义结构,不提供实现,它是一种契约,规定了对象必须满足的条件。
定义对象接口
使用 interface 关键字定义接口:
示例:
typescript
// 定义一个用户接口
interface User {
id: number;
name: string;
email: string;
}
// 使用接口
const user: User = {
id: 1,
name: "John",
email: "john@example.com"
};
// 错误:缺少必要属性
// const invalidUser: User = {
// id: 2,
// name: "Jane"
// // 缺少 email 属性
// };
// 错误:多余属性
// const extraUser: User = {
// id: 3,
// name: "Bob",
// email: "bob@example.com",
// age: 30 // 多余的 age 属性
// };可选属性
使用 ? 符号标记可选属性:
示例:
typescript
interface User {
id: number;
name: string;
email: string;
age?: number; // 可选属性
address?: string; // 可选属性
}
// 可以不提供可选属性
const user1: User = {
id: 1,
name: "John",
email: "john@example.com"
};
// 也可以提供可选属性
const user2: User = {
id: 2,
name: "Jane",
email: "jane@example.com",
age: 25,
address: "123 Main St"
};只读属性
使用 readonly 关键字标记只读属性:
示例:
typescript
interface User {
readonly id: number; // 只读属性
name: string;
email: string;
}
const user: User = {
id: 1,
name: "John",
email: "john@example.com"
};
// 可以读取只读属性
console.log(user.id); // 1
// 错误:不能修改只读属性
// user.id = 2; // Cannot assign to 'id' because it is a read-only property任意属性(索引签名)
使用索引签名定义任意属性:
示例:
typescript
// 字符串索引签名
interface User {
id: number;
name: string;
[key: string]: any; // 任意属性,键为字符串,值为任意类型
}
const user: User = {
id: 1,
name: "John",
email: "john@example.com",
age: 25,
address: "123 Main St",
// 可以添加任意其他属性
phone: "123-456-7890"
};
// 数字索引签名
interface NumberArray {
[index: number]: number; // 任意属性,键为数字,值为数字
}
const numbers: NumberArray = [1, 2, 3, 4, 5];
console.log(numbers[0]); // 1
console.log(numbers[1]); // 2接口继承
接口可以继承其他接口,使用 extends 关键字:
示例:
typescript
// 基础接口
interface Person {
id: number;
name: string;
email: string;
}
// 继承 Person 接口
interface Employee extends Person {
employeeId: number;
department: string;
position: string;
}
// 使用 Employee 接口
const employee: Employee = {
id: 1,
name: "John",
email: "john@example.com",
employeeId: 12345,
department: "IT",
position: "Software Engineer"
};
// 继承多个接口
interface Manager extends Employee {
teamSize: number;
responsibilities: string[];
}
const manager: Manager = {
id: 2,
name: "Jane",
email: "jane@example.com",
employeeId: 67890,
department: "IT",
position: "Manager",
teamSize: 10,
responsibilities: ["Manage team", "Oversee projects"]
};接口与类型别名 type 的区别
接口和类型别名都可以用来定义对象结构,但它们有一些区别:
| 特性 | 接口(interface) | 类型别名(type) |
|---|---|---|
| 声明合并 | 支持 | 不支持 |
| 继承 | 支持(extends) | 支持(交叉类型 &) |
| 实现 | 可以被类实现(implements) | 不能被类实现 |
| 扩展 | 只能扩展对象类型 | 可以扩展任何类型 |
| 语法 | 更简洁,适合对象结构 | 更灵活,适合复杂类型 |
声明合并
接口支持声明合并,而类型别名不支持:
typescript
// 接口声明合并
interface User {
id: number;
name: string;
}
interface User {
email: string;
age?: number;
}
// 合并后的 User 接口包含所有属性
const user: User = {
id: 1,
name: "John",
email: "john@example.com",
age: 25
};
// 类型别名不支持声明合并
// type User = {
// id: number;
// name: string;
// };
//
// type User = {
// email: string;
// }; // 错误:Duplicate identifier 'User'类实现
接口可以被类实现,而类型别名不能:
typescript
// 接口可以被类实现
interface Animal {
name: string;
makeSound(): void;
}
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log("Woof!");
}
}
// 类型别名不能被类实现
// type AnimalType = {
// name: string;
// makeSound(): void;
// };
//
// class Cat implements AnimalType { // 错误:Cannot implement an interface declared as a type alias
// name: string;
// constructor(name: string) {
// this.name = name;
// }
// makeSound(): void {
// console.log("Meow!");
// }
// }实战:定义接口规范数据结构
示例 1:定义 API 响应接口
typescript
// 定义用户接口
interface User {
id: number;
name: string;
email: string;
age?: number;
address?: {
street: string;
city: string;
country: string;
};
}
// 定义 API 响应接口
interface ApiResponse<T> {
success: boolean;
data?: T;
message?: string;
error?: {
code: number;
message: string;
};
}
// 使用接口
function fetchUser(): ApiResponse<User> {
return {
success: true,
data: {
id: 1,
name: "John",
email: "john@example.com",
address: {
street: "123 Main St",
city: "New York",
country: "USA"
}
}
};
}
function fetchError(): ApiResponse<User> {
return {
success: false,
message: "Failed to fetch user",
error: {
code: 404,
message: "User not found"
}
};
}
const userResponse = fetchUser();
if (userResponse.success && userResponse.data) {
console.log(userResponse.data.name); // John
}
const errorResponse = fetchError();
if (!errorResponse.success && errorResponse.error) {
console.log(errorResponse.error.message); // User not found
}示例 2:定义表单数据接口
typescript
// 定义表单数据接口
interface LoginForm {
email: string;
password: string;
rememberMe?: boolean;
}
interface RegisterForm {
name: string;
email: string;
password: string;
confirmPassword: string;
agreeToTerms: boolean;
}
// 验证登录表单
function validateLoginForm(form: LoginForm): boolean {
return form.email.length > 0 && form.password.length > 0;
}
// 验证注册表单
function validateRegisterForm(form: RegisterForm): boolean {
return (
form.name.length > 0 &&
form.email.length > 0 &&
form.password.length >= 8 &&
form.password === form.confirmPassword &&
form.agreeToTerms
);
}
// 使用
const loginForm: LoginForm = {
email: "john@example.com",
password: "password123",
rememberMe: true
};
const registerForm: RegisterForm = {
name: "John Doe",
email: "john@example.com",
password: "password123",
confirmPassword: "password123",
agreeToTerms: true
};
console.log(validateLoginForm(loginForm)); // true
console.log(validateRegisterForm(registerForm)); // true小结
本章节介绍了 TypeScript 接口的使用,包括:
- 什么是接口
- 定义对象接口
- 可选属性
- 只读属性
- 任意属性(索引签名)
- 接口继承
- 接口与类型别名的区别
- 实战:定义接口规范数据结构
接口是 TypeScript 类型系统的核心组成部分,它可以帮助你定义清晰、一致的数据结构,提高代码的可维护性和类型安全性。在实际开发中,应该充分利用接口来规范数据结构,特别是在处理 API 响应、表单数据等场景时。
