TypeScript Interface 接口详解
目录
概述
什么是接口?
接口是一种定义对象结构、函数签名或类结构的方式,它描述了对象应该具有的属性和方法。
用途
- 定义对象的形状
- 规范函数签名
- 描述类的公共部分
- 提供代码约束
基本用法
1. 对象接口
// 基本对象接口
interface Person {
name: string;
age: number;
email?: string; // 可选属性
readonly id: string; // 只读属性
}
// 使用接口
const user: Person = {
name: "John",
age: 30,
id: "123"
};
2. 函数接口
// 函数接口
interface SearchFunc {
(source: string, subString: string): boolean;
}
// 实现函数接口
const mySearch: SearchFunc = function(src: string, sub: string): boolean {
return src.search(sub) > -1;
};
3. 类接口
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date) {
this.currentTime = d;
}
}
高级特性
1. 索引签名
// 字符串索引签名
interface StringArray {
[index: number]: string;
}
// 混合类型索引
interface NumberOrStringDictionary {
[index: string]: number | string;
length: number; // ok, length 是 number 类型
name: string; // ok, name 是 string 类型
}
2. 调用签名
interface CalculateFunc {
(x: number, y: number): number;
reset(): void;
description: string;
}
let calc: CalculateFunc;
calc = function(x: number, y: number) { return x + y; } as CalculateFunc;
calc.reset = function() { /* reset logic */ };
calc.description = "Basic calculator";
3. 构造签名
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}
function createClock(
ctor: ClockConstructor,
hour: number,
minute: number
): ClockInterface {
return new ctor(hour, minute);
}
接口继承
1. 接口继承接口
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square: Square = {
color: "blue",
sideLength: 10
};
2. 多重继承
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
接口合并
1. 声明合并
// 接口自动合并
interface Box {
height: number;
width: number;
}
interface Box {
scale: number;
}
// 等同于
interface Box {
height: number;
width: number;
scale: number;
}
2. 函数重载
interface Document {
createElement(tagName: "div"): HTMLDivElement;
createElement(tagName: "span"): HTMLSpanElement;
createElement(tagName: string): HTMLElement;
}
与 Type 的对比
1. 主要区别
| 特性 | Interface | Type |
|---|---|---|
| 声明合并 | ✅ 支持 | ❌ 不支持 |
| 扩展语法 | extends | & |
| 基本类型别名 | ❌ 不支持 | ✅ 支持 |
| 联合类型 | ❌ 不直接支持 | ✅ 支持 |
| 计算属性 | ❌ 不支持 | ✅ 支持 |
2. 代码示例
// Interface
interface Animal {
name: string;
}
interface Bear extends Animal {
honey: boolean;
}
## 与 Type 的对比
### 1. 主要区别与示例
1. **声明合并**
```typescript
// Interface 支持声明合并
interface User {
name: string;
}
interface User {
age: number;
}
// 最终 User 包含 name 和 age
// Type 不支持声明合并
type Animal = {
name: string;
}
type Animal = { // 错误:标识符"Animal"重复
age: number;
}
- 扩展语法
// Interface 使用 extends
interface Animal {
name: string;
}
interface Dog extends Animal {
bark(): void;
}
// Type 使用 &
type Animal = {
name: string;
}
type Dog = Animal & {
bark(): void;
}
- 基本类型别名
// Interface 不支持基本类型别名
interface StringType = string; // 错误:不能使用
// Type 支持基本类型别名
type StringType = string; // 正确
type NumberArray = number[];
type Callback = (data: string) => void;
- 联合类型
// Interface 不能直接定义联合类型
interface Status = 'success' | 'error'; // 错误
// Type 支持联合类型
type Status = 'success' | 'error'; // 正确
type NumberOrString = number | string;
- 计算属性
// Interface 不支持计算属性
interface Fields {
[P in 'x' | 'y']: number; // 错误
}
// Type 支持计算属性
type Fields = {
[P in 'x' | 'y']: number; // 正确: { x: number; y: number; }
}
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
与抽象类的对比
1. 主要区别与示例
- 实现方式
// Interface 使用 implements
interface Animal {
name: string;
makeSound(): void;
}
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound() {
console.log('Woof!');
}
}
// Abstract Class 使用 extends
abstract class Animal {
abstract makeSound(): void;
move() {
console.log('Moving...');
}
}
class Dog extends Animal {
makeSound() {
console.log('Woof!');
}
}
- 构造函数
// Interface 不能包含构造函数实现
interface AnimalConstructor {
new (name: string): Animal; // 只能声明构造签名
}
// Abstract Class 可以包含构造函数
abstract class Animal {
protected name: string;
constructor(name: string) { // 可以包含构造函数实现
this.name = name;
}
abstract makeSound(): void;
}
- 访问修饰符
// Interface 不支持访问修饰符
interface Animal {
private name: string; // 错误:接口中不能使用访问修饰符
protected age: number; // 错误:接口中不能使用访问修饰符
makeSound(): void;
}
// Abstract Class 支持访问修饰符
abstract class Animal {
private name: string;
protected age: number;
public makeSound(): void {
console.log(this.name);
}
}
- 属性实现
// Interface 只能声明属性
interface Animal {
name: string; // 只能声明,不能初始化
}
// Abstract Class 可以包含属性实现
abstract class Animal {
protected name: string = 'Animal'; // 可以包含初始值
private age: number = 0;
static count: number = 0;
}
- 方法实现
// Interface 只能声明方法
interface Animal {
makeSound(): void; // 只能声明方法签名
move(): void;
}
// Abstract Class 可以包含具体方法实现
abstract class Animal {
abstract makeSound(): void; // 抽象方法
move(): void { // 具体方法实现
console.log('Moving...');
}
sleep(): void { // 具体方法实现
console.log('Sleeping...');
}
}
// Type
type Animal = {
name: string;
}
type Bear = Animal & {
honey: boolean;
}
6. 工具类型支持
// Type 支持复杂的工具类型
type Keys = keyof User; // 获取所有键
type ReadonlyUser = Readonly<User>; // 只读版本
type PartialUser = Partial<User>; // 可选版本
// Interface 不支持直接使用工具类型
interface UserKeys = keyof User; // 错误
interface ReadonlyUser = Readonly<User>; // 错误
- 条件类型
// Type 支持条件类型
type IsString<T> = T extends string ? true : false;
type Result = IsString<"hello">; // true
type Result2 = IsString<42>; // false
// Interface 不支持条件类型
interface StringCheck<T> = T extends string ? true : false; // 错误
- 映射类型
// Type 支持映射类型
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
type UserNullable = Nullable<User>; // 所有属性都变为可空
// Interface 不支持映射类型
interface NullableUser {
[P in keyof User]: User[P] | null; // 错误
}
与抽象类的对比
1. 主要区别
| 特性 | Interface | Abstract Class |
|---|---|---|
| 实现方式 | implements | extends |
| 构造函数 | ❌ 不能包含 | ✅ 可以包含 |
| 访问修饰符 | ❌ 不支持 | ✅ 支持 |
| 属性实现 | ❌ 只能声明 | ✅ 可以包含实现 |
| 方法实现 | ❌ 只能声明 | ✅ 可以包含实现 |
2. 代码示例
// Interface
interface Animal {
name: string;
makeSound(): void;
}
// Abstract Class
abstract class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
abstract makeSound(): void;
move(): void {
console.log("Moving...");
}
}
抽象类
基本用法
abstract class Vehicle {
constructor(protected brand: string) {}
// 抽象方法必须在子类中实现
abstract start(): void;
// 具体方法可以包含实现
stop(): void {
console.log("Vehicle stopped");
}
}
class Car extends Vehicle {
// 必须实现抽象方法
start(): void {
console.log(`${this.brand} car starting...`);
}
}
抽象类特性
- 构造函数和初始化
abstract class Database {
protected connection: Connection;
constructor(connectionString: string) {
// 可以包含初始化逻辑
this.connection = createConnection(connectionString);
this.initialize();
}
// 抽象方法强制子类实现特定的初始化逻辑
protected abstract initialize(): void;
// 具体方法提供共享功能
disconnect(): void {
this.connection.close();
}
}
class MySQLDatabase extends Database {
protected initialize(): void {
// MySQL 特定的初始化
this.connection.setDialect('mysql');
}
}
- 混合抽象和具体成员
abstract class HttpClient {
protected abstract baseUrl: string;
protected abstract headers: Record<string, string>;
// 具体方法可以使用抽象成员
async get<T>(endpoint: string): Promise<T> {
const response = await fetch(
`${this.baseUrl}${endpoint}`,
{ headers: this.headers }
);
return response.json();
}
// 抽象方法定义必须实现的行为
abstract handleError(error: Error): void;
}
class ApiClient extends HttpClient {
protected baseUrl = 'https://api.example.com';
protected headers = {
'Content-Type': 'application/json'
};
handleError(error: Error): void {
console.error('API Error:', error);
}
}
- 受保护的成员和继承
abstract class Animal {
// 受保护的属性可以被子类访问
protected abstract sound: string;
// 受保护的方法可以被子类访问和重写
protected abstract makeNoise(): void;
// 公共方法可以组合受保护的成员
public communicate(): void {
console.log('Animal is communicating...');
this.makeNoise();
}
}
class Dog extends Animal {
protected sound = 'woof';
protected makeNoise(): void {
console.log(this.sound);
}
// 可以添加额外的方法
public fetch(): void {
console.log('Dog is fetching...');
}
}
抽象类的使用场景
- 模板方法模式
abstract class DataProcessor {
// 模板方法定义算法骨架
process(data: string[]): void {
this.validate(data);
const processed = this.transform(data);
this.save(processed);
this.cleanup();
}
// 具体方法提供默认实现
protected validate(data: string[]): void {
if (!data.length) {
throw new Error('Empty data');
}
}
// 抽象方法必须由子类实现
protected abstract transform(data: string[]): number[];
// 抽象方法定义保存逻辑
protected abstract save(data: number[]): void;
// 具体方法提供清理逻辑
protected cleanup(): void {
console.log('Cleanup complete');
}
}
class FileProcessor extends DataProcessor {
protected transform(data: string[]): number[] {
return data.map(Number);
}
protected save(data: number[]): void {
// 保存到文件
console.log('Saving to file:', data);
}
}
- 框架基类
abstract class Component {
protected abstract render(): string;
protected abstract mounted(): void;
// 生命周期方法
initialize(): void {
const html = this.render();
this.updateDOM(html);
this.mounted();
}
private updateDOM(html: string): void {
// DOM 更新逻辑
}
}
class Button extends Component {
constructor(private label: string) {
super();
}
protected render(): string {
return `<button>${this.label}</button>`;
}
protected mounted(): void {
console.log('Button mounted');
}
}
最佳实践
- 抽象方法的使用
- 只声明必须由子类实现的方法
- 提供合理的默认实现
- 使用受保护的访问级别
- 抽象类的设计
- 遵循单一职责原则
- 提供清晰的抽象层次
- 合理使用具体和抽象成员
- 继承层次
- 避免深层继承
- 优先使用组合而不是继承
- 保持抽象类的专注性
- 文档和注释
/**
* 表示一个通用的数据验证器。
* 子类必须实现具体的验证逻辑。
*/
abstract class Validator<T> {
/**
* 验证给定的数据。
* @param data 要验证的数据
* @returns 验证结果
*/
abstract validate(data: T): boolean;
/**
* 获取验证错误信息。
* 子类可以重写此方法提供具体的错误信息。
*/
protected getErrorMessage(): string {
return 'Validation failed';
}
}
最佳实践
1. 何时使用 Interface
- 定义对象结构
- 定义类的公共 API
- 需要声明合并时
- 描述第三方库的类型
2. 何时使用 Type
- 需要使用联合类型或交叉类型
- 需要使用基本类型别名
- 需要使用映射类型
- 需要使用条件类型
3. 何时使用抽象类
- 需要共享代码实现
- 需要使用访问修饰符
- 需要定义构造函数
- 需要提供默认实现
实际应用示例
1. API 类型定义
interface ApiResponse<T> {
data: T;
status: number;
message: string;
timestamp: number;
}
interface UserData {
id: string;
name: string;
email: string;
}
// 使用
async function fetchUser(id: string): Promise<ApiResponse<UserData>> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
2. React 组件 Props
interface ButtonProps {
text: string;
onClick: () => void;
variant?: 'primary' | 'secondary';
disabled?: boolean;
}
const Button: React.FC<ButtonProps> = ({
text,
onClick,
variant = 'primary',
disabled = false
}) => {
return (
<button
onClick={onClick}
disabled={disabled}
className={`btn btn-${variant}`}
>
{text}
</button>
);
};
总结
-
接口的优点:
- 清晰的代码契约
- 类型安全
- 代码提示
- 可扩展性
-
使用场景:
- 定义对象结构
- API 类型声明
- 组件 Props 类型
- 类的实现约束
-
最佳实践:
- 优先使用 interface
- 合理使用继承
- 适当使用声明合并
- 保持接口简单清晰