TypeScript 类实现接口

本文献给:

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

你将学到:

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

目录

  • [一、implements 关键字](#一、implements 关键字)
    • [1.1 类可以添加额外成员](#1.1 类可以添加额外成员)
  • 二、实现多个接口
    • [2.1 接口间的属性名冲突](#2.1 接口间的属性名冲突)
    • [2.2 方法名冲突](#2.2 方法名冲突)
  • 三、类实现接口时的类型检查
    • [3.1 属性必须初始化](#3.1 属性必须初始化)
    • [3.2 方法的参数和返回值必须匹配](#3.2 方法的参数和返回值必须匹配)
    • [3.3 可选属性的处理](#3.3 可选属性的处理)
  • 四、实现接口与继承类组合
    • [4.1 接口可以继承类](#4.1 接口可以继承类)
  • 五、接口与抽象类的对比
    • [5.1 何时用接口](#5.1 何时用接口)
    • [5.2 何时用抽象类](#5.2 何时用抽象类)
  • 六、常见错误与注意事项
    • [6.1 混淆 `extends` 和 `implements`](#6.1 混淆 extendsimplements)
    • [6.2 实现接口时忘记处理可选属性](#6.2 实现接口时忘记处理可选属性)
    • [6.3 接口定义的方法在类中实现时使用了错误的 `this` 类型](#6.3 接口定义的方法在类中实现时使用了错误的 this 类型)
    • [6.4 私有字段冲突](#6.4 私有字段冲突)
    • [6.5 实现多个接口时同名方法重载顺序](#6.5 实现多个接口时同名方法重载顺序)
  • 七、综合示例
  • 八、小结

一、implements 关键字

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

typescript 复制代码
interface Drawable {
    draw(): void;
    color?: string;   // 可选属性
}

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

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

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

typescript 复制代码
interface Point {
    x: number;
    y: number;
    distance(): number;
}

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

1.1 类可以添加额外成员

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

typescript 复制代码
interface Animal {
    name: string;
}

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

二、实现多个接口

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

typescript 复制代码
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 接口间的属性名冲突

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

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

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

2.2 方法名冲突

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

typescript 复制代码
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 属性必须初始化

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

typescript 复制代码
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 方法的参数和返回值必须匹配

typescript 复制代码
interface Formatter {
    format(input: string): string;
}

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

3.3 可选属性的处理

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

typescript 复制代码
interface Logger {
    log(msg: string): void;
    level?: string;
}

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

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

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

typescript 复制代码
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 接口可以继承类

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

typescript 复制代码
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。不能混用。

typescript 复制代码
class A implements B  // B 是接口
class C extends D     // D 是类

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

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

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

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

6.4 私有字段冲突

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

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

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

七、综合示例

typescript 复制代码
// 定义两个接口
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 #面向对象 #学习笔记 #前端开发

相关推荐
小则又沐风a1 小时前
深入了解进程概念 第二章
java·linux·服务器·前端
CCPC不拿奖不改名1 小时前
PostgreSQL数据库部署linux服务器流程
linux·服务器·数据库·windows·python·docker·postgresql
lzh200409191 小时前
手搓一个简易 Linux 进程池:巩固进程知识
linux·c++
xiaoye-duck1 小时前
《Linux系统编程》Linux基础开发工具 (一):软件包管理器yum/apt,编辑器Vim,编译器GCC/G++
linux
William.csj1 小时前
Linux——服务器后台运行程序指南(包含 Python 与 .sh 脚本实战)
linux·服务器·python
杨云龙UP2 小时前
MySQL主库高峰期备份引发504故障:从库手动切换接管 + 主从恢复同步 + Docker版DB2重启实战_2026-05-17
linux·运维·数据库·mysql·docker·容器·centos
lifewange2 小时前
Vim 统一替换(全局替换)
linux·编辑器·vim
用户2367829801682 小时前
Linux netstat 命令深度解析:从网络连接到端口监控的完整实现
linux
曾帅1682 小时前
linux ubuntu 挂载硬盘
linux·运维·ubuntu