admin管理员组文章数量:1178545
The following error
Static members cannot reference class type parameters.
results from the following piece 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> {
}
/* this is what I would like, but the because is not allowed because :
"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
I think it is clear from this example what I'm trying to achieve. I want be able to call instance and static methods on my inheriting class and have the inheriting class itself as inferred type. I found the following workaround to get what I want:
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 = {} // some object that stores list 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] // abusing any here, but it works :(
}
public async static fetch<T extends Resource>(this: ResourceType<T>): Promise<T[]> {
const result = await service.get()
store(result, instanceLists) // some fn that puts it in 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 problem that I have with this is that overriding static methods becomes really tedious. Doing the following:
class Model extends Resource {
public async static fetch(): Promise<Model[]> {
return super.fetch();
}
}
will result in an error because Model
is no longer extending Resource
correctly, because of the different signature. I can't think of a way to declare a fetch method without giving me errors, let alone having an intuitive easy way to overload.
The only work around I could get to work is the following:
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 very nice.
Is there a way to override the fetch method without having to manually cast to Model and do tricks with generics?
The following error
Static members cannot reference class type parameters.
results from the following piece 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> {
}
/* this is what I would like, but the because is not allowed because :
"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
I think it is clear from this example what I'm trying to achieve. I want be able to call instance and static methods on my inheriting class and have the inheriting class itself as inferred type. I found the following workaround to get what I want:
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 = {} // some object that stores list 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] // abusing any here, but it works :(
}
public async static fetch<T extends Resource>(this: ResourceType<T>): Promise<T[]> {
const result = await service.get()
store(result, instanceLists) // some fn that puts it in 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 problem that I have with this is that overriding static methods becomes really tedious. Doing the following:
class Model extends Resource {
public async static fetch(): Promise<Model[]> {
return super.fetch();
}
}
will result in an error because Model
is no longer extending Resource
correctly, because of the different signature. I can't think of a way to declare a fetch method without giving me errors, let alone having an intuitive easy way to overload.
The only work around I could get to work is the following:
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 very nice.
Is there a way to override the fetch method without having to manually cast to Model and do tricks with generics?
Share Improve this question edited Sep 26, 2018 at 13:07 Maurits Moeys asked Sep 26, 2018 at 12:47 Maurits MoeysMaurits Moeys 1,2401 gold badge13 silver badges23 bronze badges 1 |1 Answer
Reset to default 42You could do something like this:
function Resource<T>() {
abstract class Resource {
/* static methods */
public static list: T[] = [];
public static async fetch(): Promise<T[]> {
return null!;
}
/* instance methods */
public save(): Promise<T> {
return null!
}
}
return Resource;
}
In the above Resource
is a generic function that returns a locally declared class. The returned class is not generic, so its static properties and methods have concrete types for T
. You can extend it like this:
class Model extends Resource<Model>() {
// overloading should also work
public static async fetch(): Promise<Model[]> {
return super.fetch();
}
}
Everything has the types you expect:
Model.list; // Model[]
Model.fetch(); // Promise<Model[]>
new Model().save(); // Promise<Model>
So that might work for you.
The only caveats I can see right now:
There's a bit of duplication in
class X extends Resource<X>()
which is less than perfect, but I don't think you can get contextual typing to allow the secondX
to be inferred.Locally-declared types tend not to be exportable or used as declarations, so you might need to be careful there or come up with workarounds (e.g., export some structurally-identical or structurally-close-enough type and declare that
Resource
is that type?).
Anyway hope that helps. Good luck!
本文标签: javascriptWorkaround for accessing class type arguments in static method in TypescriptStack Overflow
版权声明:本文标题:javascript - Workaround for accessing class type arguments in static method in Typescript - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1738012853a2049207.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
public async static fetch<T>()
, also seen in this related answer: stackoverflow.com/a/65804786/423171 (it may not be relevant for this specific question but it was for me when I google this error) – cprcrack Commented Apr 27, 2022 at 12:53