【发布时间】:2021-03-13 06:52:40
【问题描述】:
鉴于以下类层次结构,我是否以及如何将具体的处理程序实例类型映射到特定的事件类实例?
class Event {}
class EventA extends Event {
constructor(public readonly value: number) {
super();
}
}
class EventB extends Event {
constructor(public readonly message: string) {
super();
}
}
abstract class EventHandler<T> {
public abstract run(): T;
}
class EventAHandler extends EventHandler<number> {
constructor(private readonly event: EventA) {
super();
}
public run() {
return this.event.value + 1;
}
}
class EventBHandler extends EventHandler<string> {
constructor(private readonly event: EventB) {
super();
}
public run() {
return this.event.message + ' world!';
}
}
class EventHandlerFactory {
static createHandler<T extends Event>(event: T) {
if (event instanceof EventA) {
return new EventAHandler(event);
}
if (event instanceof EventB) {
return new EventBHandler(event);
}
throw new Error('Not implemented!');
}
}
const event = new EventA(1);
// T in createHandler is inferred as EventA
const handler = EventHandlerFactory.createHandler(event); // EventAHandler | EventBHandler
const result = handler.run(); // string | number -> should be number
我想要的是让handler 推断(或成为)EventAHandler 而不是联合类型(没有类型断言as),这样我就可以确定特定处理程序的结果类型。
【问题讨论】:
-
您可以将
createHandler的返回类型设置为EventHandler<T>,但您需要在函数本身内部使用as。 -
很难知道您的事件处理程序 在构造之后会做什么,因为在您的示例中它们没有方法。作为一般的 OOP 思想,如果类型
A或B仅对实例化的目的很重要,并且您可以返回一般的EventHandler类型,则它并不关心它是A还是B。但我不知道这在您的用例中是否有意义。 -
这是我几天前回答的关于输入抽象工厂模式的类似问题。 stackoverflow.com/a/66501501/10431574 我在函数体中声明返回类型为
EventHandler<T>并带有一个断言。 (需要,因为T可能是EventA | EventA、EventA以及一些额外的属性等)。 -
当你只有两种类型时,你可以通过使用重载来避免很多问题,但这是不可扩展的。
-
@LindaPaiste 我已经更新了我的问题。重载似乎现在可以工作,但我想知道是否有更好的方法来做到这一点......
标签: typescript generics factory-pattern