如何建模继承同一基类、带父子关系与链式方法的多类型?
实现支持链式调用与树形遍历的资源类体系
需求说明
我要做一组资源类,每个类都继承自Resource基类,得满足这些要求:
- 能链式调用:从上层资源实例创建下层实例,最后在最底层调用方法
- 支持树形结构:每个资源可以有父节点(也是
Resource的子类),也可以是没有父节点的根节点 - 向上遍历父节点时,每个层级的类型必须明确,比如知道当前是Group、Organization还是User
目标是针对Group > Organization > User这组资源,实现下面的调用:
const group = new GroupResource('someIdForConstructor') group .organization('someIdForOrgConstructor') .user('someUserIdForUserConstructor') .getName() // 向上遍历拿根节点ID group .organization('someIdForOrgConstructor') .user('someUserIdForUserConstructor') .getParent() .getParent() .getId() // 返回group的id
我自己写的代码在类型上有问题,没法明确父节点的具体类型,下面是我当前的代码:
export abstract class Resource<Parent extends Resource | null = null> { private parent: Parent // parent is either another class which extends resource or it is null private id: string; constructor(parent: Parent, id: string) { this.id = id; this.parent = parent; } public getId(): string { return this.id; } public getParent(): Parent { return this.parent; } } export class GroupResource<T extends Resource> extends Resource<T> { constructor(parent: T, id: string) { super(parent, id) } public organization(id: string): OrganizationResource<GroupResource<T>> { return new OrganizationResource<GroupResource<T>>(this, id) } } export class OrganizationResource<T extends Resource> extends Resource<T> { constructor(parent: T, id: string) { super(parent, id) } public form(id: string): UserResource<OrganizationResource<T>> { return new UserResource<OrganizationResource<T>>(this, id) } } export class UserResource<T extends Resource> extends Resource<T> { private name: string constructor(parent: T, id: string) { super(parent, id) this.name = "John" } public getName(): string { return this.name; } }
修正后的实现方案
1. 优化Resource基类
先把基类的泛型约束改精准,确保父节点只能是合法的资源实例或者null(根节点):
export abstract class Resource<Parent extends Resource<any, any> | null = null> { private readonly parent: Parent; private readonly id: string; constructor(parent: Parent, id: string) { this.id = id; this.parent = parent; } public getId(): string { return this.id; } public getParent(): Parent { return this.parent; } }
2. 实现具体资源类
每个资源类的泛型参数明确指定父节点的具体类型,链式方法返回的子资源也会绑定当前实例作为父节点,这样类型链就能完整传递:
// Group资源:可以是根节点(父为null),也可以有其他父资源 export class GroupResource<Parent extends Resource<any, any> | null = null> extends Resource<Parent> { constructor(parent: Parent, id: string) { super(parent, id); } // 创建Organization,父节点就是当前Group实例 public organization(id: string): OrganizationResource<GroupResource<Parent>> { return new OrganizationResource(this, id); } } // Organization资源:父节点是任意Resource子类,链式返回User实例 export class OrganizationResource<Parent extends Resource<any, any>> extends Resource<Parent> { constructor(parent: Parent, id: string) { super(parent, id); } // 原代码方法名是form,改成user匹配需求示例 public user(id: string): UserResource<OrganizationResource<Parent>> { return new UserResource(this, id); } } // User资源:父节点是任意Resource子类,有自己的getName方法 export class UserResource<Parent extends Resource<any, any>> extends Resource<Parent> { private readonly name: string; constructor(parent: Parent, id: string) { super(parent, id); this.name = "John"; } public getName(): string { return this.name; } }
3. 验证调用效果
现在完全能实现需求里的调用,而且每个步骤的类型都明确:
// 创建根Group节点(无父节点) const group = new GroupResource(null, 'someIdForConstructor'); // 链式调用拿用户名 const userName = group .organization('someIdForOrgConstructor') .user('someUserIdForUserConstructor') .getName(); // 类型是string,值为"John" // 向上遍历拿Group的ID const groupId = group .organization('someIdForOrgConstructor') .user('someUserIdForUserConstructor') .getParent() // 类型是OrganizationResource<GroupResource<null>> .getParent() // 类型是GroupResource<null> .getId(); // 返回"someIdForConstructor"
核心改进点
- 泛型参数精准传递父节点类型,
getParent()返回的实例类型完全明确,不会丢失类型信息 - 链式方法返回的子资源实例,明确绑定当前实例作为父节点,保证整个调用链的类型完整性
- 支持传入
null作为根节点的父节点,符合树形结构的逻辑
内容的提问来源于stack exchange,提问作者paullc
相关产品推荐
相关产品推荐

