An issue has arisen due to the code below
"Static members cannot reference class type parameters."
This problem originates from the following snippet of code
abstract class Resource<T> {
/* static methods */
public static list: T[] = [];
public async static fetch(): Promise<T[]> {
this.list = await service.get();
return this.list;
}
/* instance methods */
public save(): Promise<T> {
return service.post(this);
}
}
class Model extends Resource<Model> {
}
/* The desired outcome is as follows, but it's not permissible due to:
"Static members cannot reference class type parameters."
*/
const modelList = await Model.fetch() // inferred type would be Model[]
const availableInstances = Model.list // inferred type would be Model[]
const savedInstance = modelInstance.save() // inferred type would be Model
From this example, it can be inferred what I am aiming for. I wish to have the ability to call both instance and static methods on my inheriting class while having the inheriting class itself as the inferred type. To achieve this, I have found a workaround:
interface Instantiable<T> {
new (...args: any[]): T;
}
interface ResourceType<T> extends Instantiable<T> {
list<U extends Resource>(this: ResourceType<U>): U[];
fetch<U extends Resource>(this: ResourceType<U>): Promise<U[]>;
}
const instanceLists: any = {} // an object that stores lists with constructor.name as key
abstract class Resource {
/* static methods */
public static list<T extends Resource>(this: ResourceType<T>): T[] {
const constructorName = this.name;
return instanceLists[constructorName] // using 'any' here, although effective :(
}
public async static fetch<T extends Resource>(this: ResourceType<T>): Promise<T[]> {
const result = await service.get()
store(result, instanceLists) // function that adds to instanceLists
return result;
}
/* instance methods */
public save(): Promise<this> {
return service.post(this);
}
}
class Model extends Resource {
}
/* now inferred types are correct */
const modelList = await Model.fetch()
const availableInstances = Model.list
const savedInstance = modelInstance.save()
The issue with this approach is that overriding static methods becomes cumbersome. For example:
class Model extends Resource {
public async static fetch(): Promise<Model[]> {
return super.fetch();
}
}
This will lead to an error because Model
no longer correctly extends Resource
due to the differing signature. There seems to be no straightforward way to declare a fetch method without errors or an intuitive mechanism for overloading.
The only working solution I could come up with is:
class Model extends Resource {
public async static get(): Promise<Model[]> {
return super.fetch({ url: 'custom-url?query=params' }) as Promise<Model[]>;
}
}
In my opinion, this is not an elegant solution.
Is there a more efficient way to override the fetch method without resorting to manual casting to Model and complicated generic manipulations?