如何在抽象父类中为实现打字稿制作泛型方法?

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&lt;typeof EntityInstance&gt; 的类型很奇怪;它根本不必是类构造函数。而InstanceType&lt;FooInstance&gt; 也不是很明智。如果您可以让您的示例代码在运行时实际执行您所要求的,我可以帮助输入。但现在除了需要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

以上是关于如何在抽象父类中为实现打字稿制作泛型方法?的主要内容,如果未能解决你的问题,请参考以下文章

类泛型类型的打字稿方法允许任何

如何将方法的泛型类型限制为打字稿中的对象?

打字稿泛型,获取数组内容的类型

带有约束的打字稿泛型不能分配给泛型接口

打字稿:使父类中的公共方法成为派生类中的私有/受保护方法

打字稿泛型类参数