I am trying to create and model the types for a series of classes which can be called in a chain in order to execute a method on the deepest class. Each of these classes represents a resource
, and should extend a Resource
class which has some basic methods that each of these classes should possess at minimum. Each resource can also have a parent (in the tree traversal node sense) which is another Resource
, or no parent at all (i.e. tree root node). This should be reflected in the Resource class being extended. Each class which extends the Resource class may be the child of a different Resource-extending class (i.e. User can have Organization as a parent but another instance of User can have as its parent some other arbitrary class which extends Resource). My end goal, for a set of example resources [Group, Organization, User]
related like so Group > Organization > User
, is to be able to call these resources like this:
const group = new GroupResource('someIdForConstructor')
group
.organization('someIdForOrgConstructor')
.user('someUserIdForUserConstructor')
.getName()
// or go back up by getting the parents
group
.organization('someIdForOrgConstructor')
.user('someUserIdForUserConstructor')
.getParent()
.getParent()
.getId() // returns the id of the group
I'm unsure of how to write the classes and types such that User
, Organization
, and Group
extend Resource
, while also having a parent which also extends the Resource
class, all while being able to know the type of the parent.
Essentially i want to be able to "traverse" these classes as if they were each a node in a tree, and be able to go traverse all the way back up by calling parent
all while knowing at each level what type the current node is (i.e. group, organization, user).
This is what I have so far; it doesn't work type-wise but it illustrates the relationships I would like to have:
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;
}
}