如何在抽象父 class 中创建通用方法来实现打字稿?

how make a generic method in a abstract parent class for implementation typescript?

在 TypeScript 中实现这一点的最佳和(正确)方法是什么。 $Foo.getInstance('uid') 应该 return FooInstance 基于实现?

我想抽象地 class Entity 一个从池中获取实例的方法, 对于 return 实施的 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; 不会产生错误。

您可以尝试将 Entity 中的输入更改为以下内容:

abstract class Entity {
    abstract Instance: new () => EntityInstance;

    instances: Record<string,
        InstanceType<this["Instance"]> | undefined
    > = {};
}

Instance 属性 是一个 constructor that returns an EntityInstance (or a subtype of it). And the instances property type depends on the type of Instance; by using 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