TypeScript Interface 接口详解

135 阅读6分钟

TypeScript Interface 接口详解

目录

  1. 概述
  2. 基本用法
  3. 高级特性
  4. 接口继承
  5. 接口合并
  6. 与 Type 的对比
  7. 与抽象类的对比

概述

什么是接口?

接口是一种定义对象结构、函数签名或类结构的方式,它描述了对象应该具有的属性和方法。

用途

  1. 定义对象的形状
  2. 规范函数签名
  3. 描述类的公共部分
  4. 提供代码约束

基本用法

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. 主要区别

特性InterfaceType
声明合并✅ 支持❌ 不支持
扩展语法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;
}
  1. 扩展语法
// Interface 使用 extends
interface Animal {
  name: string;
}
interface Dog extends Animal {
  bark(): void;
}

// Type 使用 &
type Animal = {
  name: string;
}
type Dog = Animal & {
  bark(): void;
}
  1. 基本类型别名
// Interface 不支持基本类型别名
interface StringType = string;  // 错误:不能使用

// Type 支持基本类型别名
type StringType = string;  // 正确
type NumberArray = number[];
type Callback = (data: string) => void;
  1. 联合类型
// Interface 不能直接定义联合类型
interface Status = 'success' | 'error';  // 错误

// Type 支持联合类型
type Status = 'success' | 'error';  // 正确
type NumberOrString = number | string;
  1. 计算属性
// 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. 主要区别与示例

  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!');
  }
}
  1. 构造函数
// Interface 不能包含构造函数实现
interface AnimalConstructor {
  new (name: string): Animal;  // 只能声明构造签名
}

// Abstract Class 可以包含构造函数
abstract class Animal {
  protected name: string;
  
  constructor(name: string) {  // 可以包含构造函数实现
    this.name = name;
  }
  
  abstract makeSound(): void;
}
  1. 访问修饰符
// 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);
  }
}
  1. 属性实现
// Interface 只能声明属性
interface Animal {
  name: string;  // 只能声明,不能初始化
}

// Abstract Class 可以包含属性实现
abstract class Animal {
  protected name: string = 'Animal';  // 可以包含初始值
  private age: number = 0;
  static count: number = 0;
}
  1. 方法实现
// 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>;  // 错误
  1. 条件类型
// 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;  // 错误
  1. 映射类型
// 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. 主要区别

特性InterfaceAbstract Class
实现方式implementsextends
构造函数❌ 不能包含✅ 可以包含
访问修饰符❌ 不支持✅ 支持
属性实现❌ 只能声明✅ 可以包含实现
方法实现❌ 只能声明✅ 可以包含实现

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...`);
  }
}

抽象类特性

  1. 构造函数和初始化
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');
  }
}
  1. 混合抽象和具体成员
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);
  }
}
  1. 受保护的成员和继承
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...');
  }
}

抽象类的使用场景

  1. 模板方法模式
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);
  }
}
  1. 框架基类
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');
  }
}

最佳实践

  1. 抽象方法的使用
  • 只声明必须由子类实现的方法
  • 提供合理的默认实现
  • 使用受保护的访问级别
  1. 抽象类的设计
  • 遵循单一职责原则
  • 提供清晰的抽象层次
  • 合理使用具体和抽象成员
  1. 继承层次
  • 避免深层继承
  • 优先使用组合而不是继承
  • 保持抽象类的专注性
  1. 文档和注释
/**
 * 表示一个通用的数据验证器。
 * 子类必须实现具体的验证逻辑。
 */
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>
  );
};

总结

  1. 接口的优点:

    • 清晰的代码契约
    • 类型安全
    • 代码提示
    • 可扩展性
  2. 使用场景:

    • 定义对象结构
    • API 类型声明
    • 组件 Props 类型
    • 类的实现约束
  3. 最佳实践:

    • 优先使用 interface
    • 合理使用继承
    • 适当使用声明合并
    • 保持接口简单清晰