在 Typescript 中访问静态方法中的类类型参数的解决方法 [英] Workaround for accessing class type arguments in static method in Typescript
问题描述
以下错误
静态成员不能引用类类型参数.
Static members cannot reference class type parameters.
以下代码的结果
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();
}
}
将导致错误,因为 Model
不再正确扩展 Resource
,因为签名不同.我想不出一种方法来声明一个 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.
有没有办法覆盖 fetch 方法而不必手动转换为 Model 并使用泛型做技巧?
Is there a way to override the fetch method without having to manually cast to Model and do tricks with generics?
推荐答案
你可以这样做:
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;
}
在上面的 Resource
中是一个通用函数,它返回一个 本地声明的类.返回的类是非泛型的,所以它的静态属性和方法具有T
的具体类型.你可以像这样扩展它:
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>
所以这可能对你有用.
我现在能看到的唯一警告:
The only caveats I can see right now:
class X extends Resource<X>()
中有一些重复,这并不完美,但我认为您无法获得上下文类型以允许第二个X
被推断.
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.
本地声明的类型往往不可导出或用作声明,因此您可能需要在那里小心或想出解决方法(例如,导出一些结构相同或结构足够接近的类型并声明Resource
是那种类型?).
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!
这篇关于在 Typescript 中访问静态方法中的类类型参数的解决方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!