如何使用具有泛型类型的静态属性并有意义?
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 <T>
属性?
这个变量是一个猫鼬连接。
保存单例变量以供将来访问是否有意义?
【问题讨论】:
你能不能试着把它变成一个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<MyType>(args);
一样指定类型 T
但通常您不需要,因为您的构造函数签名告诉 typescript 用于创建实例的参数与泛型 T
的关系.
通过您的构造函数constructor(entity: T, collectionName: string)
,您告诉打字稿“获取变量entity
的类型并将其用作此实例的通用T
”。所以当你调用const myObj = new ConcreteRepository(myEntity, myCollectionName);
创建一个新实例时,typescript会判断myObj
的类型为ConcreteRepository<typeof myEntity>
。 (我使用名称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
,它返回一个正确类型T
的SessionDb
。我们已将该实现留给子类来解决。但是既然我们知道这个方法一定存在,那么基类在做this.model = this.getConnection().getModel(collectionName);
的时候调用它是安全的
【讨论】:
解决方案1更接近我的问题!谢谢! :)以上是关于如何使用具有泛型类型的静态属性并有意义?的主要内容,如果未能解决你的问题,请参考以下文章