如何使用具有泛型类型的静态属性并有意义?

Posted

技术标签:

【中文标题】如何使用具有泛型类型的静态属性并有意义?【英文标题】:How to use a static attribute with generics type and make sense? 【发布时间】:2021-01-21 22:17:32 【问题描述】:

我有一个构造函数并调用静态方法来设置一个静态变量。

export abstract class AbstractRepository<T extends Typegoose> 
  private static conn: SessionDb<T>; // <-- Compile error here!
  protected model: Model<InstanceType<T>, >;

  constructor(entity: T, collectionName: string)
    if(AbstractRepository.conn == null) AbstractRepository.conn = SessionDb.connect(entity);
    this.model = AbstractRepository.conn.getModel(collectionName);
    

如何键入conn: SessionDb &lt;T&gt; 属性? 这个变量是一个猫鼬连接。 保存单例变量以供将来访问是否有意义?

【问题讨论】:

你能不能试着把它变成一个minimal reproducible example,它可以放到像The TypeScript Playground这样的独立IDE中?或者也许提供一个指向 Web IDE 中的项目的链接,以显示您在做什么?在运行时只有一个AbstractRepository 而没有T,所以你的单例会被设置一次,然后再也不会。如果您希望将其设置为“每 XXX 一次”,您需要确定 XXX 是什么,并使实际具有支持它的范围的代码。也许你想要一个产生类的工厂函数?如果没有minimal reproducible example,真的不确定。 如果没有更多信息(特别是SessionDb 的定义),我真的无能为力,但正如@jcalz 所说,只有一个static conn 变量适用于所有类型T ,因此使该变量的类型依赖于T 似乎是错误的。由于这是一个抽象类,您可能希望将conn 设为抽象变量并强制您的具体实现它。但是,既然您问“保存一个单例变量以供将来访问是否有意义?”答案是如果conn 依赖于T,那么它不会。只需将其设为实例变量即可。 优秀的答案!我同意我可以获得更多信息(包括使用 IDE),但我的想法是讨论如何以及是否有意义维护一个“类型化”静态变量,如果是,如何键入它。我相信最好的实现将是一个单例实例变量,正如@linda-paiste 所建议的那样。你能按照这个想法制定一个答案吗? 【参考方案1】:

问题

当您有一个泛型类时,泛型参数T 适用于该类的每个实例。当您创建泛型类的实例时,您可以像 const myObj = new GenericClass&lt;MyType&gt;(args); 一样指定类型 T 但通常您不需要,因为您的构造函数签名告诉 typescript 用于创建实例的参数与泛型 T 的关系.

通过您的构造函数constructor(entity: T, collectionName: string),您告诉打字稿“获取变量entity 的类型并将其用作此实例的通用T”。所以当你调用const myObj = new ConcreteRepository(myEntity, myCollectionName);创建一个新实例时,typescript会判断myObj的类型为ConcreteRepository&lt;typeof myEntity&gt;。 (我使用名称ConcreteRepository 来描述扩展AbstractRepository 的类,因为抽象类无法实例化)。

同时,static 属性适用于类本身,因此适用于每个实例

您可以有多个 AbstractRepository 实例,它们可以有许多不同的 T 值,但它们都将共享相同的 private static conn 值。该变量将具有首先构造的特定实例的 T 类型(因为它只设置一次),不是当前实例的 T 类型。

拥有一个依赖于通用 T 的静态变量根本没有意义。

解决方案 1

为了保证每个实例都有一个与其实体类型对应的SessionDb,我们将conn做成实例变量而不是静态变量。每个实例在构造函数中将其设置为this.conn

export abstract class AbstractRepository<T extends Typegoose> 
  protected conn: SessionDb<T>;
  protected model: Model<InstanceType<T>, >;

  constructor(entity: T, collectionName: string)
    this.conn = SessionDb.connect(entity);
    this.model = this.conn.getModel(collectionName);
  

通过这种设置,基类可以由本身是通用的类来实现:

export class ConcreteRepository<T extends Typegoose> extends AbstractRepository<T> 

或者通过绑定到特定值T的类:

export class SpecificRepository extends AbstractRepository<SpecificType> 

解决方案 2

AbstractRepository 是一个abstract 类,这意味着它不能直接实例化,只能通过扩展它的类实例化来创建。如果这些扩展类都只适用于特定类型的实体,例如上面的 SpecificRepository 示例,那么您可以为每个特定扩展类设置一个单独的 static conn 变量。

它看起来有点像这样(我不知道其中一些类型的细节):

export abstract class AbstractRepository<T extends Typegoose> 
  protected model: Model<InstanceType<T>, >;

  constructor(entity: T, collectionName: string)
    this.model = this.getConnection().getModel(collectionName);
  

  abstract getConnection(): SessionDb<T>;


export class SpecificRepository extends AbstractRepository<SpecificType> 
    private static conn: SessionDb<SpecificType>;

    getConnection(): SessionDb<SpecificType> 
        if( SpecificRepository.conn === undefined) 
            // would need to know about enity somehow, either by storing this.entity 
            // or by having a static entity property on the class
            SpecificRepository.conn = SessionDb.connect(entity);
        
        return SpecificRepository.conn;
    

在这里,基类声明每个实体必须有一个方法getConnection,它返回一个正确类型TSessionDb。我们已将该实现留给子类来解决。但是既然我们知道这个方法一定存在,那么基类在做this.model = this.getConnection().getModel(collectionName);的时候调用它是安全的

【讨论】:

解决方案1更接近我的问题!谢谢! :)

以上是关于如何使用具有泛型类型的静态属性并有意义?的主要内容,如果未能解决你的问题,请参考以下文章

如何在实现c#非泛型接口泛型属性时跳过传递类型?

Java泛型和内部类

如何使用泛型类型的子类初始化泛型属性?

如何调用具有泛型类型的结构的关联函数?

Python 3 类型,具有任意数量的包含类型的自定义可变参数泛型,如何?

C#实体类中如何定义泛型集合类型的属性?