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 混淆 extends 和 implements)
    • [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";  // 联合类型可同时满足
}

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

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 支持 private、protected
多继承 一个类可实现多个接口 一个类只能继承一个抽象类
存储状态(字段) 只能声明,不能初始化 可以声明并初始化字段
运行时存在 不存在(编译后消失) 存在(编译为 JS 类)

5.1 何时用接口

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

5.2 何时用抽象类

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

六、常见错误与注意事项

6.1 混淆 extends 和 implements

类继承使用 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 #面向对象 #学习笔记 #前端开发

相关推荐
tiankongdeyige20 小时前
游戏战斗系统学习第2篇:有限状态机 (FSM)
学习·游戏·typescript
夜听莺儿鸣1 天前
502-003_Linux 中断与异常(一):从Linux角度理解中断与异常
linux·中断与异常·linux中断
杨云龙UP1 天前
TDengine 3.4.2.8 Community 三节点三副本生产集群部署实战(DNode/MNode/taosAdapter/Explorer)
大数据·linux·运维·数据库·tdengine·时序库
向成科技1 天前
XC3576H工控主板|深度适配Ubuntu 26.04 LTS,释放边缘AI与工业开发新潜能
linux·人工智能·ubuntu·机器人·硬件·主板·边缘ai
Fcy6481 天前
Linux下 进程间关系与守护进程
linux·运维·服务器·守护进程
码农小韩1 天前
Linux驱动理论(二)——Linux字符设备驱动
linux·嵌入式软件开发·linux操作系统·linux应用开发·linux驱动理论
well06121 天前
Linux粘滞位与Makefile机制深度解析
linux·运维·服务器
再写一行代码就下班1 天前
linux sh脚本在windows修改导致无法使用解决方式
java·linux·centos
额额额对了1 天前
SPI通信
linux·c语言·汇编·单片机·嵌入式硬件·arm
FYKJ_20101 天前
django电影推荐系统55066-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django