如何在NestJS TypeScript中实现一个类继承多个类?
TypeScript实现类多继承需求的解决方案
问题描述
现有如下TypeScript代码,想要创建User类同时继承UserRegisterParams和Common类,拿到两者所有属性和方法:
export class UserInitialParams { @ApiProperty({example: 'john@doe.com', description: 'Email'}) @Prop({required: true}) email: string; @ApiProperty({example: 'admin', description: 'Role'}) @Prop({required: true, default: 'admin'}) role: string; } export class UserRegisterParams extends UserInitialParams{ @ApiProperty({example: 'asd123456', description: 'Password'}) @Prop({required: true}) password: string; } @Schema({timestamps: true}) export class Common { @Prop({ required: false, default: null }) deletedAt: Date | null; @Prop({ required: false, default: false }) isDeleted: boolean | null; softDelete: Function; unDelete: Function; isOwner: Function; constructor() { this.deletedAt = null; this.isDeleted = false; } }
想写的代码(但TypeScript不支持这种多继承语法):
export class User extends UserRegisterParams, Common {}
解决方案
TypeScript本身不支持类的多继承,但可以用下面两种方式实现需求:
方法1:把Common改成接口+实现类
TypeScript允许类继承一个父类的同时实现多个接口。可以先把Common的属性和方法定义成接口,再用一个类实现这个接口,最后在User里继承UserRegisterParams并实现接口,同时复用Common的方法逻辑:
// 定义Common接口,规范属性和方法 export interface ICommon { deletedAt: Date | null; isDeleted: boolean | null; softDelete(): void; unDelete(): void; isOwner(): boolean; } // 保留Common类作为接口的实现,复用方法逻辑 @Schema({timestamps: true}) export class Common implements ICommon { @Prop({ required: false, default: null }) deletedAt: Date | null; @Prop({ required: false, default: false }) isDeleted: boolean | null; constructor() { this.deletedAt = null; this.isDeleted = false; } softDelete(): void { this.deletedAt = new Date(); this.isDeleted = true; } unDelete(): void { this.deletedAt = null; this.isDeleted = false; } isOwner(): boolean { // 这里写你的业务逻辑 return true; } } // 创建User类:继承UserRegisterParams,实现ICommon接口 export class User extends UserRegisterParams implements ICommon { @Prop({ required: false, default: null }) deletedAt: Date | null; @Prop({ required: false, default: false }) isDeleted: boolean | null; softDelete: () => void; unDelete: () => void; isOwner: () => boolean; constructor() { super(); // 把Common的实例方法和属性复制到当前实例 Object.assign(this, new Common()); } }
方法2:使用Mixin混入模式
Mixin是TypeScript里用来复用多个类功能的常用模式,适合需要整合多个类逻辑的场景:
// 先定义一个构造函数类型,方便后续处理 type Constructor<T = {}> = new (...args: any[]) => T; // 写一个工具函数,用来把混入类的属性和方法挂载到目标类原型上 function applyMixin<T extends Constructor<any>>(baseClass: T, mixins: Constructor<any>[]) { mixins.forEach(mixin => { // 复制原型上的方法 Object.getOwnPropertyNames(mixin.prototype).forEach(propName => { if (propName !== 'constructor') { baseClass.prototype[propName] = mixin.prototype[propName]; } }); // 复制实例属性 const mixinInstance = new mixin(); Object.getOwnPropertyNames(mixinInstance).forEach(propName => { baseClass.prototype[propName] = mixinInstance[propName]; }); }); } // 创建User类,先继承UserRegisterParams export class User extends UserRegisterParams { @Prop({ required: false, default: null }) deletedAt: Date | null; @Prop({ required: false, default: false }) isDeleted: boolean | null; softDelete: Function; unDelete: Function; isOwner: Function; constructor() { super(); this.deletedAt = null; this.isDeleted = false; } } // 把Common类的功能混入到User里 applyMixin(User, [Common]);
注意点
- 因为你用了
@Prop(Mongoose)和@ApiProperty(Swagger)这些装饰器,所以User里的属性必须手动加上对应的装饰器,不然Mongoose不会把这些字段映射到数据库,Swagger也生成不了正确的API文档。 - 如果两个父类有同名的属性或方法,得手动处理冲突,避免被意外覆盖。
内容的提问来源于stack exchange,提问作者sundowatch
相关产品推荐
相关产品推荐

