在TypeScript中,我有一个扩展模块Events
的类,它在声明中包含以下内容:
on(event: string | symbol, listener: (...args: any[]) => void): this;
扩展模块的类会发出许多事件,这些事件对于侦听器具有不同的签名。我可以为此属性创建多个覆盖,稍微更具体,但仍然匹配签名。就像是:
export class Agent extends Events {
constructor(conf: IAgentConf);
on(event: 'eventA', listener: (body: IAEvent) => void): this
on(event: 'eventB', listener: (body: IPayload<IBEvent>) => void):this;
on(event: 'eventC', listener: (body: ICEvent[]) => void): this;
...
}
使用这种类型,TypeScript可以在声明事件侦听器时识别回调的形状。
但是,当我进一步扩展此对象时,我遇到了一个问题,一个新对象发出了一个新事件:
class MyAgent extends Agent {
static EventD: string = 'EventD';
init: () => void;
on(event: 'EventD', listener: (body: IEventD) => void):this;
constructor(conf: IAgentConf) {
super(conf);
this.init = () => {
this.on('EventA', body => {
this.emit(MyAgent.EventD, body.thingy);
});
};
init();
}
}
遗憾的是,这不起作用。我收到错误:
(TS) Property 'on' in type 'MyAgent' is not assignable to the same property in base type 'Agent'.
是否有可能进一步覆盖孙子类中祖父母类的属性?
通常,只要子类的属性可以赋给父类的属性,子类就可以覆盖超类的属性(或方法)。对于具有多个呼叫签名的方法,我认为规则是超类中的每个呼叫签名必须在子类中具有可分配给它的相应呼叫签名。这种“可转让的”关系有点泥泞,因为the parameters are compared bivariantly。
对于在您的情况下兼容的方法,每个类都需要重新声明超类中的所有事件类型以及一般调用签名:
class Agent extends Events {
on(event: 'eventA', listener: (body: IAEvent) => void): this
on(event: 'eventB', listener: (body: IPayload<IBEvent>) => void):this;
on(event: 'eventC', listener: (body: ICEvent[]) => void): this;
on(event: string | symbol, listener: (...args: any[]) => void): this;
on(event: string | symbol, listener: (...args: any[]) => void): this {
return super.on(event, listener);
}
}
class MyAgent extends Agent {
on(event: 'eventA', listener: (body: IAEvent) => void): this
on(event: 'eventB', listener: (body: IPayload<IBEvent>) => void):this;
on(event: 'eventC', listener: (body: ICEvent[]) => void): this;
on(event: 'EventD', listener: (body: IEventD) => void):this;
on(event: string | symbol, listener: (...args: any[]) => void): this;
on(event: string | symbol, listener: (...args: any[]) => void): this {
return super.on(event, listener);
}
}
您可以通过为on
方法定义一个接口并扩展它来减少重复:
interface EventsOn<This> {
(event: string | symbol, listener: (...args: any[]) => void): This;
}
class Events {
on(event: string | symbol, listener: (...args: any[]) => void): this {
// ...
return this;
}
}
interface AgentOn<This> extends EventsOn<This> {
(event: 'eventA', listener: (body: IAEvent) => void): This;
(event: 'eventB', listener: (body: IPayload<IBEvent>) => void):This;
(event: 'eventC', listener: (body: ICEvent[]) => void): This;
}
interface Agent {
on: AgentOn<this>;
}
class Agent extends Events { }
interface MyAgentOn<This> extends AgentOn<This> {
(event: 'EventD', listener: (body: IEventD) => void):This;
}
interface MyAgent {
on: MyAgentOn<this>;
}
class MyAgent extends Agent { }
(当您为具有调用签名的函数类型声明接口时,扩展接口似乎会累积调用签名,而当您使用on
方法声明接口时,扩展接口似乎会丢失来自超级接口的调用签名。)