TypeScript: TypeScript 类与接口
最后更新:2026-08-26
类和接口是 TypeScript 面向对象编程的两大支柱——类提供实现,接口定义契约。当类实现接口时,TypeScript 会确保类满足接口规定的所有要求。
1. 类实现接口(implements)
(1) 基本语法
TYPESCRIPT
interface Printable {
toString(): string;
}
class User implements Printable {
constructor(public name: string, public age: number) {}
toString(): string {
return `${this.name},${this.age}岁`;
}
}
let user = new User("Charlie", 20);
console.log(user.toString()); // "Charlie,20岁"
(2) 实现接口的意义
接口是"契约"——类实现接口就承诺"我会提供接口要求的所有属性和方法":
TYPESCRIPT
interface Animal {
name: string;
speak(): string;
move(distance: number): void;
}
class Dog implements Animal {
constructor(public name: string) {}
speak(): string {
return "汪汪!";
}
move(distance: number): void {
console.log(`${this.name} 移动了 ${distance} 米`);
}
}
// ❌ 缺少接口要求的成员会报错
// class Cat implements Animal {
// constructor(public name: string) {}
// // 缺少 speak 和 move → 编译错误
// }
(3) 多接口实现
一个类可以实现多个接口——用逗号分隔:
TYPESCRIPT
interface Serializable {
serialize(): string;
}
interface Comparable {
compareTo(other: this): number;
}
class Score implements Serializable, Comparable {
constructor(public value: number) {}
serialize(): string {
return JSON.stringify({ value: this.value });
}
compareTo(other: Score): number {
return this.value - other.value;
}
}
let s1 = new Score(90);
let s2 = new Score(85);
console.log(s1.serialize()); // '{"value":90}'
console.log(s1.compareTo(s2)); // 5(正数表示 s1 > s2)
▶ 示例:用接口定义统一的API
TYPESCRIPT
interface Repository<T> {
findById(id: number): T | null;
findAll(): T[];
save(entity: T): void;
delete(id: number): boolean;
}
interface User {
id: number;
name: string;
email: string;
}
class UserRepository implements Repository<User> {
private users: User[] = [];
findById(id: number): User | null {
return this.users.find(u => u.id === id) ?? null;
}
findAll(): User[] {
return [...this.users];
}
save(entity: User): void {
let index = this.users.findIndex(u => u.id === entity.id);
if (index >= 0) {
this.users[index] = entity; // 更新
} else {
this.users.push(entity); // 新增
}
}
delete(id: number): boolean {
let len = this.users.length;
this.users = this.users.filter(u => u.id !== id);
return this.users.length < len;
}
}
let repo = new UserRepository();
repo.save({ id: 1, name: "Charlie", email: "xiao@example.com" });
repo.save({ id: 2, name: "Diana", email: "hong@example.com" });
console.log(repo.findById(1)?.name); // "Charlie"
console.log(repo.findAll().length); // 2
输出:
TEXT
📖 仅展示
Charlie
2
2. 接口继承类
TypeScript 的独特特性——接口可以继承类的成员定义(只继承类型声明,不继承实现):
TYPESCRIPT
class Control {
private state: string = "active";
protected getState(): string {
return this.state;
}
}
// 接口继承类——只继承类型签名
interface Selectable extends Control {
select(): void;
}
// 类实现接口时必须提供所有成员
class Dropdown implements Selectable {
private state: string = "active"; // 必须自己实现 private 成员
protected getState(): string {
return this.state;
}
select(): void {
console.log("已选中:" + this.getState());
}
}
💡 用途: 接口继承类主要在复杂类层次中用于描述"具有某个类结构同时有额外能力"的类型。日常开发中用得不多。
3. 抽象类(abstract)
抽象类是"半成品类"——它提供部分实现,子类必须补全剩余部分:
(1) 基本语法
TYPESCRIPT
abstract class Shape {
// 抽象方法——没有实现,子类必须实现
abstract getArea(): number;
abstract getPerimeter(): number;
// 具体方法——有实现,子类直接继承
describe(): string {
return `面积:${this.getArea().toFixed(2)},周长:${this.getPerimeter().toFixed(2)}`;
}
}
class Circle extends Shape {
constructor(public radius: number) {
super();
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
getPerimeter(): number {
return 2 * Math.PI * this.radius;
}
}
class Rectangle extends Shape {
constructor(public width: number, public height: number) {
super();
}
getArea(): number {
return this.width * this.height;
}
getPerimeter(): number {
return 2 * (this.width + this.height);
}
}
// ❌ 抽象类不能直接实例化
// let shape = new Shape();
let circle = new Circle(5);
let rect = new Rectangle(4, 6);
console.log(circle.describe()); // "面积:78.54,周长:31.42"
console.log(rect.describe()); // "面积:24.00,周长:20.00"
(2) 抽象类 vs 接口
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 实现方法 | 可以有具体实现 | 不能有任何实现 |
| 构造函数 | 可以有 | 不能有 |
| 多继承 | 只能单继承 | 可以多继承(extends) |
| 访问修饰符 | 支持 public/private/protected | 属性默认 public |
| 实例化 | 不能直接实例化 | 不能实例化(纯类型) |
| 运行时存在 | 是(编译为 JS 类) | 否(纯类型声明) |
(3) 什么时候用抽象类
- 多个子类共享相同的部分实现(代码复用)
- 需要 protected 成员让子类访问
- 需要构造函数逻辑
(4) 什么时候用接口
- 只需要定义"能做什么"(能力描述)
- 类可能需要实现多个能力(多接口实现)
- 不需要共享实现代码
- 需要声明合并
▶ 示例:抽象类实现模板方法模式
TYPESCRIPT
abstract class DataParser {
// 模板方法——定义算法骨架
parse(input: string): string {
let raw = this.read(input);
let validated = this.validate(raw);
let transformed = this.transform(validated);
return this.format(transformed);
}
// 具体方法——所有子类共享
private read(input: string): string {
return input.trim();
}
// 抽象方法——子类各自实现
protected abstract validate(data: string): string;
protected abstract transform(data: string): string;
// 钩子方法——子类可选覆写
protected format(data: string): string {
return data;
}
}
class UpperParser extends DataParser {
protected validate(data: string): string {
if (data.length === 0) throw new Error("输入为空");
return data;
}
protected transform(data: string): string {
return data.toUpperCase();
}
}
class ReverseParser extends DataParser {
protected validate(data: string): string {
return data;
}
protected transform(data: string): string {
return data.split("").reverse().join("");
}
protected format(data: string): string {
return `[反转] ${data}`;
}
}
let upper = new UpperParser();
let reverse = new ReverseParser();
console.log(upper.parse(" hello world ")); // "HELLO WORLD"
console.log(reverse.parse(" hello world ")); // "[反转] dlrow olleh"
输出:
TEXT
📖 仅展示
HELLO WORLD
[反转] dlrow olleh
4. 类作为类型使用
类定义本身就是一个类型——可以用类名作为变量的类型:
(1) 类的类型含义
TYPESCRIPT
class Point {
constructor(public x: number, public y: number) {}
distanceTo(other: Point): number {
return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2);
}
}
// 类作为类型——接受 Point 实例或结构兼容的对象
let p1: Point = new Point(1, 2);
let p2: Point = { x: 3, y: 4 }; // ✅ 结构兼容(但不包含方法!)
console.log(p1.distanceTo(p2)); // ✅ p1 有 distanceTo 方法
// p2.distanceTo(p1); // ❌ p2 只有 x 和 y,没有方法
⚠️ 注意: 类的类型部分只包含实例属性和方法签名。用结构兼容的对象赋值时,方法不会被包含——只有属性匹配。需要方法时应该用
new Point() 创建实例。
(2) 类类型 vs typeof 类
TYPESCRIPT
class Factory {
static create(): Factory {
return new Factory();
}
constructor(public name: string) {}
}
// Factory 类型——实例类型
let instance: Factory = new Factory("产品");
// typeof Factory 类型——类本身(构造函数类型)
let FactoryClass: typeof Factory = Factory;
let another = FactoryClass.create(); // ✅ 调用静态方法
❓ 常见问题
Q implements 和 extends 有什么区别?
A implements 是"实现接口"——类承诺提供接口要求的所有成员,但不继承任何实现代码。extends 是"继承类"——子类获得父类的所有实现代码,并可以覆写。一个类可以 implements 多个接口,但只能 extends 一个父类。
Q 抽象类和接口该用哪个?
A 简单规则——如果多个类需要共享实现代码,用抽象类;如果只需要定义"能做什么"的契约,用接口。可以两者结合——接口定义能力,抽象类提供部分实现。
Q 类实现接口时可以有多余的成员吗?
A 可以。类实现接口只要求"至少有接口要求的成员",多余成员完全合法。例如
interface Printable { print(): void } 和 class Document implements Printable { print() {...}; save() {...} }——Document 多了 save 方法,但完全合法。Q 接口能继承抽象类吗?
A 可以。
interface ISelect extends AbstractControl {} 合法——接口会继承抽象类的所有成员签名(包括 private 成员的签名)。但实现这个接口的类必须自己提供所有成员的实现,包括 private 成员。📖 小节
- implements 让类承诺实现接口的所有成员——TypeScript 编译时检查是否满足
- 一个类可以 implements 多个接口,用逗号分隔
- 接口可以 extends 类——只继承类型签名,不继承实现
- 抽象类(abstract)提供部分实现,子类必须实现剩余的抽象方法
- 抽象类适合共享实现代码,接口适合定义能力契约;两者可以结合使用
- 类定义本身就是类型——可以作为变量的类型注解使用
📝 作业
- 基础题(难度⭐):定义
Loggable接口(log 方法),让User类和Product类分别实现它。创建实例并调用 log 方法。 - 进阶题(难度⭐⭐):定义抽象类
Vehicle(brand 属性、abstract start 方法、concrete describe 方法),然后创建Car和Bike子类分别实现 start。写一个函数接受 Vehicle 类型参数调用 describe。 - 挑战题(难度⭐⭐⭐):设计一个
SortableCollection<T>接口(length 属性、compare 方法、swap 方法),让NumberCollection类实现它。然后在Sorter抽象类中实现冒泡排序算法(依赖接口方法),子类只需提供 compare 和 swap 的具体实现。