本文献给:

已掌握 TypeScript 接口定义、可选属性、只读属性、继承等知识的开发者。本文将讲解类如何通过 implements 关键字实现一个或多个接口,以及类实现接口与抽象类的区别,为后续面向对象编程篇章打下基础。


你将学到:

  1. implements 关键字的基本用法
  2. 一个类实现多个接口
  3. 类实现接口时的类型检查规则
  4. 接口与抽象类的对比与选择
  5. 实现接口与继承类的组合使用



一、implements 关键字

接口描述了类的公共契约(public contract)。类可以使用 implements 关键字声明它必须实现某个接口的所有成员。

interface Drawable {
    draw(): void;
    color?: string;   // 可选属性
}

class Circle implements Drawable {
    draw() {
        console.log("Drawing circle");
    }
    // color 是可选的,可以不实现
}

const c: Drawable = new Circle();
c.draw();

如果类没有完整实现接口定义的成员,TypeScript 会报错。

interface Point {
    x: number;
    y: number;
    distance(): number;
}

class MyPoint implements Point {
    x = 0;
    y = 0;
    // 缺少 distance 方法 ❌
}

1.1 类可以添加额外成员

实现接口的类可以拥有接口中未定义的额外属性和方法。

interface Animal {
    name: string;
}

class Dog implements Animal {
    name = "Buddy";
    bark() {           // 额外方法
        console.log("woof");
    }
}

二、实现多个接口

一个类可以实现多个接口,用逗号分隔。

interface Printable {
    print(): void;
}

interface Serializable {
    toJSON(): string;
}

class Report implements Printable, Serializable {
    print() {
        console.log("Printing report");
    }
    toJSON() {
        return JSON.stringify({ content: "report" });
    }
}

2.1 接口间的属性名冲突

如果两个接口有同名但类型不同的属性,类必须实现一个同时满足两种类型的成员。这通常意味着需要联合类型或无法满足。

interface A {
    value: string;
}
interface B {
    value: number;
}
class C implements A, B {
    value: string | number = "hello";  // 联合类型可同时满足
}

如果类型完全不兼容(如 stringnumber 没有交集),则无法实现。

2.2 方法名冲突

如果两个接口有同名方法但签名不同,类中需要实现为重载。

interface Clickable {
    click(): void;
}
interface Draggable {
    click(x: number, y: number): void;
}
class UIElement implements Clickable, Draggable {
    click(x?: number, y?: number): void {
        if (x !== undefined && y !== undefined) {
            console.log(`Dragged to ${x},${y}`);
        } else {
            console.log("Clicked");
        }
    }
}

三、类实现接口时的类型检查

3.1 属性必须初始化

接口中声明的属性,在类中必须被初始化(构造函数中赋值或声明时赋默认值),或标记为可选。

interface Config {
    url: string;
    timeout: number;
}

class HttpClient implements Config {
    url: string;
    timeout: number;
    constructor(url: string) {
        this.url = url;
        this.timeout = 5000;  // 初始化
    }
}

3.2 方法的参数和返回值必须匹配

interface Formatter {
    format(input: string): string;
}

class UpperFormatter implements Formatter {
    format(input: number): number {  // ❌ 参数类型不匹配
        return input;
    }
}

3.3 可选属性的处理

接口中的可选属性,类可以不实现。

interface Logger {
    log(msg: string): void;
    level?: string;
}

class ConsoleLogger implements Logger {
    log(msg: string) {
        console.log(msg);
    }
    // level 未实现,允许
}

四、实现接口与继承类组合

类可以同时继承一个父类并实现一个或多个接口。继承在前,implements 在后。

class BaseEntity {
    id: number = 0;
}

interface Timestamped {
    createdAt: Date;
    updatedAt: Date;
}

class User extends BaseEntity implements Timestamped {
    name: string;
    createdAt: Date;
    updatedAt: Date;
    constructor(name: string) {
        super();
        this.name = name;
        this.createdAt = new Date();
        this.updatedAt = new Date();
    }
}

4.1 接口可以继承类

如之前所学,接口可以继承类,然后由另一个类实现。但这要求实现类必须是该类的子类。

class Control {
    private state: boolean;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() {}
}
// class Fake implements SelectableControl {} // ❌ 不是 Control 子类

五、接口与抽象类的对比

接口和抽象类都用于定义契约,但差异明显。

特性接口抽象类
实例化不能不能
实现方法体不能(TS 接口只有声明)可以有具体方法
构造器可以有
访问修饰符所有成员隐含 public支持 privateprotected
多继承一个类可实现多个接口一个类只能继承一个抽象类
存储状态(字段)只能声明,不能初始化可以声明并初始化字段
运行时存在不存在(编译后消失)存在(编译为 JS 类)

5.1 何时用接口

  • 描述对象的结构(类似于“鸭子类型”)
  • 需要让一个类实现多个契约
  • 与第三方库或 API 定义类型(轻量、无运行时开销)
  • 类的实现完全不相关,只关心公共方法签名

5.2 何时用抽象类

  • 需要提供部分默认实现
  • 需要共享字段(状态)给子类
  • 需要 protected 成员或构造函数逻辑
  • 需要运行时存在(如 instanceof 检查)

六、常见错误与注意事项

6.1 混淆 extendsimplements

类继承使用 extends,实现接口使用 implements。不能混用。

class A implements B  // B 是接口
class C extends D     // D 是类

6.2 实现接口时忘记处理可选属性

接口有可选属性,类可以不实现,但如果类中实现了该属性,类型必须匹配。

6.3 接口定义的方法在类中实现时使用了错误的 this 类型

方法签名中的 this 参数需要匹配。通常不需要显式写。

6.4 私有字段冲突

接口不能包含私有字段,因此无法强制类实现私有成员。这是接口与抽象类的关键区别。

6.5 实现多个接口时同名方法重载顺序

重载签名需要正确排列,实现签名必须兼容所有重载。


七、综合示例

// 定义两个接口
interface Eater {
    eat(food: string): void;
}

interface Sleeper {
    sleep(hours: number): void;
}

interface Reproducible {
    reproduce(): void;
}

// 抽象类实现部分默认行为
abstract class Animal implements Eater, Sleeper {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    
    eat(food: string) {
        console.log(`${this.name} eats ${food}`);
    }
    
    sleep(hours: number) {
        console.log(`${this.name} sleeps for ${hours} hours`);
    }
    
    abstract makeSound(): void;  // 抽象方法
}

// 具体类
class Dog extends Animal implements Reproducible {
    makeSound() {
        console.log("Woof!");
    }
    
    reproduce() {
        console.log(`${this.name} gives birth to puppies`);
    }
    
    // 额外方法
    fetch() {
        console.log(`${this.name} fetches the ball`);
    }
}

class Cat extends Animal {
    makeSound() {
        console.log("Meow!");
    }
    
    // Cat 不实现 Reproducible
}

// 使用
const dog = new Dog("Buddy");
dog.eat("bone");
dog.sleep(8);
dog.makeSound();
dog.reproduce();
dog.fetch();

const cat = new Cat("Whiskers");
cat.eat("fish");

// 接口作为类型约束
function takeNap(animal: Sleeper) {
    animal.sleep(1);
}
takeNap(dog);
takeNap(cat);

// 多个接口组合的场景:可序列化的用户
interface Serializable {
    serialize(): string;
}

interface Identifiable {
    id: number;
}

class User implements Identifiable, Serializable {
    id: number;
    name: string;
    constructor(id: number, name: string) {
        this.id = id;
        this.name = name;
    }
    serialize(): string {
        return JSON.stringify({ id: this.id, name: this.name });
    }
}

const u = new User(1, "Alice");
console.log(u.serialize());

八、小结

概念语法示例说明
实现单接口class A implements B必须实现 B 的所有成员
实现多接口class A implements B, C必须实现 B 和 C 的所有成员
继承+实现class A extends B implements C先继承再实现
可选接口属性类可以不实现,但如果实现必须类型匹配提升灵活性
接口 vs 抽象类接口纯契约(无实现),抽象类可含实现和状态按需选择



觉得文章有帮助?别忘了:

👍 点赞 👍 – 给我一点鼓励
⭐ 收藏 ⭐ – 方便以后查看
🔔 关注 🔔 – 获取更新通知



标签: #TypeScript #类实现接口 #implements #面向对象 #学习笔记 #前端开发

更多推荐