在Typescript中以静态方法访问类类型参数的解决方法 [英] Workaround for accessing class type arguments in static method in Typescript

查看:336
本文介绍了在Typescript中以静态方法访问类类型参数的解决方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下错误


静态成员无法引用类类型参数。


来自以下代码片段

 抽象类资源< T> {
/ *静态方法* /
公共静态列表:T [] = [];
public async static fetch():Promise< T []> {
this.list = await service.get();
返回this.list;
}
/ *实例方法* /
public save():Promise< T> {
return service.post(this);
}
}

class模型扩展资源< Model> {
}

/ *这是我想要的,但不允许因为:
静态成员不能引用类类型参数。
* /

const modelList = await Model.fetch()//推断类型将是Model []
const availableInstances = Model.list //推断类型将是Model [ ]
const savedInstance = modelInstance.save()//推断类型将是Model

I从这个例子可以清楚地看出我正在努力实现的目标。我希望能够在我的继承类上调用实例和静态方法,并将继承类本身作为推断类型。我找到了以下解决方法来获得我想要的东西:

  interface Instantiable< T> {
new(... args:any []):T;
}
interface ResourceType< T>扩展Instantiable< T> {
list< U extends Resource>(this:ResourceType< U>):U [];
fetch< U extends Resource>(this:ResourceType< U>):Promise< U []> ;;
}

const instanceLists:any = {} //某个对象以constructor.name为键存储列表

abstract class Resource {
/ *静态方法* /
公共静态列表< T extends Resource>(this:ResourceType< T>):T [] {
const constructorName = this.name;
返回instanceLists [constructorName] //滥用任何此处,但它可以工作:(
}
public async static fetch< T extends Resource>(this:ResourceType< T>):Promise< T []> {
const result = await service.get()
store(result,instanceLists)//将一些fn放入instanceLists
返回结果;
}
/ *实例方法* /
public save():Promise< this> {
return service.post(this);
}
}
class模型扩展资源{
}
/ *现在推断的类型是正确的* /
const modelList = await Model.fetch()
const availableInstances = Model.list
const savedInstance = modelInstance.save()

我遇到的问题是覆盖静态方法变得真的执行以下操作:

  class Model extends Resource {

public async static fetch():无极<模型[]> {
return super.fetch();
}
}

会导致错误,因为模型不再正确地扩展资源,因为签名不同。我想不出一种方法来声明一个fetch方法而不会给我带来错误,更不用说有一种直观的简单方法来重载。



唯一的解决方法我可以得到工作如下:

  class Model extends Resource {
public async static get():Promise< Model [ ] GT; {
将super.fetch({url:'custom-url?query = params'})返回为Promise< Model []> ;;
}
}

在我看来,这不是很好。



有没有办法覆盖fetch方法而不必手动转换为Model并使用泛型做技巧?

  function Resource< T>() {
abstract class Resource {
/ * static methods * /
public static list:T [] = [];
public static async fetch():Promise< T []> {
返回null!;
}
/ *实例方法* /
public save():Promise< T> {
返回null!
}
}
返回资源;
}

在上面资源是一个泛型函数,返回本地宣布的课程。返回的类不是泛型,因此其静态属性和方法具有 T 的具体类型。您可以像这样扩展:

  class Model extends Resource< Model>(){
// overloading也应该work
public static async fetch():Promise< Model []> {
return super.fetch();
}
}

一切都有你期望的类型:

  Model.list; // Model [] 
Model.fetch(); //承诺< Model []>
new Model()。save(); //承诺<模型>

这可能适合你。



我现在能看到的唯一警告:




  • 这里有中的一些重复类X扩展了资源< X>()这不完美,但我不认为你可以通过上下文输入来允许第二个 X 将被推断。


  • 本地声明的类型往往不可导出或用作声明,所以你可能需要小心那里或提出解决方法(例如,导出一些结构相同或结构上足够接近的类型,并声明资源是那种类型?)。




无论如何希望有所帮助。祝你好运!


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?

解决方案

You 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 second X 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!

这篇关于在Typescript中以静态方法访问类类型参数的解决方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆