在打字稿的基类构造函数参数中引用子类的实例类型



我正在 Typescript 3.4.2 和 Node v.12 中研究事件溯源原型。

它运行良好,但我想避免为每种类型的事件编写复制粘贴的构造函数和泛型,如 AbstractEvent<T>

有没有办法基于子类实例在父类构造函数中启用 TS 类型检查?

abstract class AbstractEvent {
    // I want type system work here something like:
    // public constructor(data: InstanceType< typeof this >)
    public constructor(data: any) {
        for (const key in data) {
            this[key] = data[key];
        }
    }
}
class PackCreatedEvent extends AbstractEvent {
    public readonly id: string;
    public readonly title: string;
    // I want to avoid writing constructors for each event type
    // and also trying to avoid extra interfaces
    public constructor(data: PackCreatedEvent) {
        super(data);
    }
}
class UserCreatedEvent extends AbstractEvent {
    public readonly id: string;
    public readonly email: string;
}
// instantiating
const packCreatedEvent1 = new PackCreatedEvent({
    id: 'GSNV-FGHT-FGHF-HFTH',
    title: 'Apple',
});
const packCreatedEvent2 = new PackCreatedEvent({
    id: 'ABGF-FGHT-FGHF-HFTH',
    title: 'Banana',
    // uncomment to see error
    // someBadProperty: 'test',
});

const userCreatedEvent = new UserCreatedEvent({
    id: 'GSNV-FGHT-FGHF-HFTH',
    email: 'test@mail.com',
    // not a valid property
    // and not error because of 'any' in AbstractEvent.constructor
    someBadProperty: 'test',
});

构造函数中没有访问派生类的内置方法。多态this在构造函数中无法访问,如注释中所述。

最简单的选项是将类型参数添加到 AbstractEvent 。类型参数将是派生类中的类本身。这样做的优点是解决方案不需要任何额外的运行时代码。不需要派生类型构造函数。

abstract class AbstractEvent<T extends AbstractEvent<any>> {
    public constructor(data: T) {
        for (const key in data) {
            this[key as any] = data[key];
        }
    }
}
class PackCreatedEvent extends AbstractEvent<PackCreatedEvent> {
    public readonly id: string;
    public readonly title: string;
}
class UserCreatedEvent extends AbstractEvent<UserCreatedEvent> {
    public readonly id: string;
    public readonly email: string;
}
const userCreatedEvent = new UserCreatedEvent({
    id: 'GSNV-FGHT-FGHF-HFTH',
    email: 'test@mail.com',
    // not a valid property
    // error now
    someBadProperty: 'test',
});

最新更新