如何在抽象父类中为实现打字稿制作泛型方法?
Posted
技术标签:
【中文标题】如何在抽象父类中为实现打字稿制作泛型方法?【英文标题】:how make a generic method in a abstract parent class for implementation typescript? 【发布时间】:2021-06-29 16:47:49 【问题描述】:在 TypeScript 中实现这一点的最佳和(正确)方法是什么。
$Foo.getInstance('uid')
应该根据实现返回 FooInstance
吗?
我希望在抽象类Entity
中提供一种从池中获取实例的方法,
用于返回实现的EntityInstance
。
abstract class Entity
abstract Instance: Partial<typeof EntityInstance>;
instances: [uid: string]: EntityInstance = ;
getInstance (uid: string )
return this.instances[uid]
abstract class EntityInstance
prop='';
class Foo extends Entity
Instance = FooInstance // @implementation
class FooInstance extends EntityInstance
const $Foo = new Foo();
// need return InstanceType<FooInstance>
const instance = $Foo.getInstance('uid');
所以这里的例子:
const instance = $Foo.getInstance('uid')
应该是 FooInstance
;
但它实际上是一个EntityInstance
这是正确的!
所以我尝试将此方法 getInstance
更改为类似的方法。
getInstance <t=this>(uid: string ): InstanceType<this['Instance']>
return this.instances[uid]
它工作!:) 但会产生一些错误类型。 我是 ts 文档的菜鸟,我可以做些什么改变来使这个逻辑正常工作。 我知道 ts 很强大,但不知道如何使它在我的 ide 中正常工作。
最小的生殖演示typescript
我希望myInstance.__foo2;
不会产生错误。
【问题讨论】:
Please replace/supplement images of errors with plain text versions when asking a question. 请考虑修改此问题中的代码以构成minimal reproducible example,将其放入像The TypeScript Playground 这样的独立IDE 中时,可以清楚地说明您面临的问题。这将使那些想要帮助您的人立即着手解决问题,而无需首先重新创建它。它将使您得到的任何答案都可以针对定义明确的用例进行测试。 现在,我不明白你为什么期望FooInstance
出来;该实现将索引到空的instances
对象并返回undefined
。这既不是FooInstance
,也不是EntityInstance
。而Partial<typeof EntityInstance>
的类型很奇怪;它根本不必是类构造函数。而InstanceType<FooInstance>
也不是很明智。如果您可以让您的示例代码在运行时实际执行您所要求的,我可以帮助输入。但现在除了需要this
类型之外,我无法理解意图是什么。
感谢@jcalz,这对我来说是最小的可复制性,但有时很难将复杂的事情转化为简单的事情。这有帮助吗,我制作了另一个更通用的版本? tinyurl.com/yzk5j7nh
所以我想要一个让myInstance.__foo2;
工作的方法
【参考方案1】:
您可以尝试将Entity
中的类型更改为以下内容:
abstract class Entity
abstract Instance: new () => EntityInstance;
instances: Record<string,
InstanceType<this["Instance"]> | undefined
> = ;
Instance
属性是一个constructor,它返回一个EntityInstance
(或其子类型)。而instances
的属性类型取决于Instance
的类型;通过使用polymorphic this
,我们说对于Entity
的任何子类,instances
属性将取决于同一子类中Instance
的类型。
这为您提供了您正在寻找的行为,至少对于示例代码:
class Foo extends Entity
Instance = FooInstance;
class FooInstance extends EntityInstance
__foo2 = 2;
const $Foo = new Foo();
$Foo.instances['myUid'] = new $Foo.Instance();
const myInstance = $Foo.instances['myUid'];
myInstance.__foo2; // okay
请注意,在子类本身内部使用多态 this
类型可能有点麻烦:
class Foo extends Entity
Instance = FooInstance;
constructor()
super();
this.instances.abc = new FooInstance(); // error!
this.instances.abc = new this.Instance(); // error!
我不确定你是否需要做这样的事情,但是尝试在 Foo
内的 this.instances
上设置一个属性会失败,因为编译器不知道如果有人出现并且 this
会是什么子类Foo
。它将this
视为未指定的泛型类型,并且无法验证是否可以为其分配任何特定值。在这种情况下,您可能需要使用type assertions 来抑制错误。
另一种方法是将Entity
设为generic class,其中类型参数T
对应于子类中EntityInstance
的特定子类型:
abstract class Entity<T extends EntityInstance>
abstract Instance: new () => T;
instances: Record<string, T | undefined> = ;
任何特定的子类都需要指定 T
应该是什么(这有点多余),但随后一切正常……子类内部和外部:
class Foo extends Entity<FooInstance>
Instance = FooInstance;
constructor()
super();
this.instances.abc = new FooInstance(); // okay
this.instances.abc = new this.Instance(); // okay
myInstance.__foo2; // still okay
Playground link to code
【讨论】:
Oki 非常感谢@jcalz 提供的高质量文档!,非常好的解释和建议。所以对两者都进行了很多实验,我保留了最后的解决方案,因为更接近我当前的需求并且更具可读性!我对最后一个解决方案还有一点问题。如果你可以看一个更复杂的例子。我不确定为什么我会在那里出错并且没有足够的实验。如果你有时间,我会继续在文档中搜索:)tinyurl.com/4f39wte4
以上是关于如何在抽象父类中为实现打字稿制作泛型方法?的主要内容,如果未能解决你的问题,请参考以下文章