类和接口是 TypeScript 中两种重要的类型定义方式。理解它们的区别和配合使用方式,对于编写健壮的 TypeScript 代码至关重要。
implements 回顾#
类使用 implements 实现接口的契约:
// TypeScript 5.x
interface Printable { print(): void}
interface Loggable { log(message: string): void}
// 实现多个接口class Document implements Printable, Loggable { constructor(private content: string) {}
print(): void { console.log(this.content) }
log(message: string): void { console.log(`[Document] ${message}`) }}
const doc = new Document('Hello, World!')doc.print()doc.log('Created')类作为类型#
类声明会创建两个东西:
- 构造函数:用于创建实例
- 类型:描述实例的结构
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 作为类型使用function printPoint(p: Point): void { console.log(`(${p.x}, ${p.y})`)}
// 任何满足 Point 结构的对象都可以const point: Point = new Point(1, 2)const fakePoint = { x: 3, y: 4, distanceTo: (other: Point) => 0 }
printPoint(point) // ✅printPoint(fakePoint) // ✅ 结构兼容类型 vs 值#
class User { constructor( public name: string, public age: number ) {}}
// 作为类型:描述实例形状const user: User = new User('张三', 25)
// 作为值:构造函数本身const UserClass = Userconst user2 = new UserClass('李四', 30)
// typeof 获取构造函数类型type UserConstructor = typeof User// new (name: string, age: number) => User
function createUser(ctor: UserConstructor, name: string, age: number): User { return new ctor(name, age)}
const user3 = createUser(User, '王五', 35)接口描述类的形状#
接口可以描述类实例的形状:
interface ClockInterface { currentTime: Date setTime(d: Date): void}
class Clock implements ClockInterface { currentTime: Date = new Date()
setTime(d: Date): void { this.currentTime = d }}接口描述构造函数#
// 描述实例interface ClockInterface { tick(): void}
// 描述构造函数interface ClockConstructor { new (hour: number, minute: number): ClockInterface}
// 工厂函数使用构造函数接口function createClock( Ctor: ClockConstructor, hour: number, minute: number): ClockInterface { return new Ctor(hour, minute)}
class DigitalClock implements ClockInterface { constructor(h: number, m: number) { // 初始化 }
tick(): void { console.log('beep beep') }}
class AnalogClock implements ClockInterface { constructor(h: number, m: number) { // 初始化 }
tick(): void { console.log('tick tock') }}
const digital = createClock(DigitalClock, 12, 30)const analog = createClock(AnalogClock, 7, 45)类表达式#
// 命名类表达式const Rectangle = class RectangleClass { constructor( public width: number, public height: number ) {}
getArea(): number { return this.width * this.height }}
// 匿名类表达式const Square = class { constructor(public size: number) {}
getArea(): number { return this.size * this.size }}
// 实现接口的类表达式interface Shape { getArea(): number}
const Circle: new (radius: number) => Shape = class { constructor(public radius: number) {}
getArea(): number { return Math.PI * this.radius ** 2 }}接口 vs 抽象类#
| 特性 | 接口 | 抽象类 |
|---|---|---|
| 实现 | 无(纯类型) | 可以有部分实现 |
| 多继承 | 支持多实现 | 只能单继承 |
| 字段 | 无初始值 | 可以有初始值 |
| 构造函数 | 无 | 可以有 |
| 访问修饰符 | 无 | 支持 |
// 接口:纯契约interface Flyable { fly(): void altitude: number}
// 抽象类:可以有实现abstract class Aircraft { abstract fly(): void
protected speed: number = 0
accelerate(amount: number): void { this.speed += amount console.log(`Speed: ${this.speed}`) }}
// 结合使用class Airplane extends Aircraft implements Flyable { altitude: number = 0
fly(): void { console.log('Airplane flying') }}混入模式(Mixins)#
TypeScript 可以通过混入实现类似多继承的效果:
// 混入类型type Constructor<T = {}> = new (...args: any[]) => T
// 时间戳混入function Timestamped<TBase extends Constructor>(Base: TBase) { return class extends Base { createdAt = new Date() updatedAt = new Date()
touch() { this.updatedAt = new Date() } }}
// 标签混入function Tagged<TBase extends Constructor>(Base: TBase) { return class extends Base { tags: string[] = []
addTag(tag: string) { this.tags.push(tag) } }}
// 基类class User { constructor(public name: string) {}}
// 应用混入const TaggedTimestampedUser = Tagged(Timestamped(User))
const user = new TaggedTimestampedUser('张三')user.addTag('admin')user.touch()
console.log(user.name) // 张三console.log(user.tags) // ['admin']console.log(user.createdAt) // Date依赖注入模式#
// 服务接口interface Logger { log(message: string): void}
interface Database { query(sql: string): Promise<any[]>}
// 实现class ConsoleLogger implements Logger { log(message: string): void { console.log(message) }}
class MySQLDatabase implements Database { async query(sql: string): Promise<any[]> { console.log(`Executing: ${sql}`) return [] }}
// 使用依赖注入class UserService { constructor( private logger: Logger, private database: Database ) {}
async getUsers(): Promise<any[]> { this.logger.log('Fetching users...') return this.database.query('SELECT * FROM users') }}
// 组装const logger = new ConsoleLogger()const database = new MySQLDatabase()const userService = new UserService(logger, database)工厂模式#
interface Product { name: string price: number describe(): string}
interface ProductFactory { create(name: string, price: number): Product}
class Book implements Product { constructor( public name: string, public price: number, public author: string ) {}
describe(): string { return `《${this.name}》 by ${this.author} - ¥${this.price}` }}
class Electronics implements Product { constructor( public name: string, public price: number, public warranty: number ) {}
describe(): string { return `${this.name} - ¥${this.price} (${this.warranty}年保修)` }}
class BookFactory implements ProductFactory { constructor(private defaultAuthor: string) {}
create(name: string, price: number): Book { return new Book(name, price, this.defaultAuthor) }}
class ElectronicsFactory implements ProductFactory { constructor(private defaultWarranty: number) {}
create(name: string, price: number): Electronics { return new Electronics(name, price, this.defaultWarranty) }}
// 使用const bookFactory = new BookFactory('未知作者')const electronicsFactory = new ElectronicsFactory(1)
const book = bookFactory.create('TypeScript 指南', 99)const phone = electronicsFactory.create('智能手机', 4999)
console.log(book.describe())console.log(phone.describe())实际应用:服务层设计#
// 仓储接口interface Repository<T> { findById(id: string): Promise<T | null> findAll(): Promise<T[]> save(entity: T): Promise<T> delete(id: string): Promise<void>}
// 实体interface User { id: string name: string email: string}
// 服务接口interface UserService { getUser(id: string): Promise<User | null> getAllUsers(): Promise<User[]> createUser(data: Omit<User, 'id'>): Promise<User> deleteUser(id: string): Promise<void>}
// 仓储实现class InMemoryUserRepository implements Repository<User> { private users: Map<string, User> = new Map()
async findById(id: string): Promise<User | null> { return this.users.get(id) || null }
async findAll(): Promise<User[]> { return Array.from(this.users.values()) }
async save(user: User): Promise<User> { this.users.set(user.id, user) return user }
async delete(id: string): Promise<void> { this.users.delete(id) }}
// 服务实现class UserServiceImpl implements UserService { constructor(private repository: Repository<User>) {}
async getUser(id: string): Promise<User | null> { return this.repository.findById(id) }
async getAllUsers(): Promise<User[]> { return this.repository.findAll() }
async createUser(data: Omit<User, 'id'>): Promise<User> { const user: User = { id: Math.random().toString(36).slice(2), ...data, } return this.repository.save(user) }
async deleteUser(id: string): Promise<void> { return this.repository.delete(id) }}
// 使用const repository = new InMemoryUserRepository()const service = new UserServiceImpl(repository)
async function main() { const user = await service.createUser({ name: '张三', email: 'zhangsan@example.com', }) console.log('Created:', user)
const users = await service.getAllUsers() console.log('All users:', users)}常见问题#
🙋 何时用接口,何时用类?#
- 接口:定义契约、描述数据结构
- 类:需要实例化、有状态和行为
- 抽象类:需要部分实现、强制继承关系
🙋 类实现接口时,接口的属性会成为类的属性吗?#
不会自动成为,需要手动声明:
interface Named { name: string}
// ❌ 错误:缺少 name 属性// class Person implements Named { }
// ✅ 必须手动声明class Person implements Named { name: string = ''}🙋 如何检查一个对象是否实现了某个接口?#
TypeScript 的接口是编译时概念,运行时不存在。需要用类型守卫:
interface Flyable { fly(): void}
function isFlyable(obj: unknown): obj is Flyable { return typeof obj === 'object' && obj !== null && 'fly' in obj}
if (isFlyable(someObject)) { someObject.fly()}总结#
| 使用场景 | 选择 |
|---|---|
| 描述数据结构 | 接口 |
| 定义服务契约 | 接口 |
| 需要实例化 | 类 |
| 需要部分实现 | 抽象类 |
| 依赖注入 | 接口 + 类 |
| 工厂模式 | 接口 + 类 |
下一篇我们将进入泛型的学习,掌握 TypeScript 中最强大的类型工具之一。