扩展覆盖

时间:2018-08-30 04:09:26

标签: typescript override

在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'.

是否可以进一步覆盖孙类中的祖父母类的属性?

1 个答案:

答案 0 :(得分:1)

通常,只要子类的属性可分配给父类的属性,则子类可以覆盖超类的属性(或方法)。对于具有多个调用签名的方法,我相信规则是,超类中的每个调用签名在子类中必须具有可分配给它的相应调用签名。这种“可分配的”关系有点混乱,因为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方法声明接口时,扩展接口似乎会丢失调用来自超级接口的签名。)