如何在TypeScript中设置子类属性不同的继承类?
用泛型基类解决子类属性差异化的类型约束问题
核心解决方案:泛型基类绑定子类属性类型
将BaseClass定义为泛型类,让它与子类的属性类型关联,这样create和update方法就能自动适配不同子类的属性结构,实现严格的类型校验。
第一步:重构泛型基类
class BaseClass<T> { // 静态create方法:约束props为子类属性类型,返回子类实例 static create<T>(this: new () => BaseClass<T>, props: T): BaseClass<T> { const instance = new this(); Object.assign(instance, props); return instance; } // 实例update方法:接收部分属性参数(Partial<T>自动生成可选属性) update(props: Partial<T>): void { Object.assign(this, props); } }
这里的this: new () => BaseClass<T>是构造函数类型约束,确保create方法被子类调用时,this指向子类的构造函数,从而返回正确的子类实例类型,避免出现基类实例的类型偏差。
第二步:子类继承时绑定属性类型
子类继承BaseClass时,传入对应的属性类型作为泛型参数,同时保持类自身的属性定义:
type SubClassOneProps = { firstName: string lastName: string } class SubClassOne extends BaseClass<SubClassOneProps> { firstName!: string lastName!: string } type SubClassTwoProps = { streetName: string streetNumber: number } class SubClassTwo extends BaseClass<SubClassTwoProps> { streetName!: string streetNumber!: number }
第三步:验证类型支持
现在调用create和update时,TypeScript会自动校验参数的类型和结构:
// ✅ 类型正确,自动提示属性字段 const obj1 = SubClassOne.create({firstName: "Bob", lastName: "Doe"}); // ✅ 支持部分更新,只传入需要修改的属性 obj1.update({lastName: "Smith"}); // ✅ 自动适配SubClassTwo的属性结构 const obj2 = SubClassTwo.create({streetName: "Sunset Blvd", streetNumber: 100}); obj2.update({streetNumber: 200}); // ❌ 类型错误:缺少必填的streetNumber参数 SubClassTwo.create({streetName: "Sunset Blvd"}); // ❌ 类型错误:传入了子类不存在的属性 obj1.update({age: 30});
后续元数据需求的提前准备
如果要给属性添加「是否公开」的元数据,可以用TypeScript装饰器实现:
- 定义一个装饰器标记属性的公开状态
- 在基类中实现
toJSON()方法,根据元数据筛选公开属性返回
示例思路(核心逻辑):
// 定义装饰器标记公开属性 function Public() { return (target: any, propertyKey: string) => { Reflect.defineMetadata('public', true, target, propertyKey); }; } class BaseClass<T> { // ... 原有create、update方法 toJSON(): Partial<T> { const result: Record<string, any> = {}; // 遍历实例属性,筛选标记为公开的字段 for (const key of Object.keys(this)) { if (Reflect.getMetadata('public', this, key)) { result[key] = this[key]; } } return result as Partial<T>; } } // 子类中使用装饰器标记公开属性 class SubClassOne extends BaseClass<SubClassOneProps> { @Public() firstName!: string; lastName!: string; // 未标记则视为非公开字段 }
内容的提问来源于stack exchange,提问作者user1923052
相关产品推荐
相关产品推荐

