【问题标题】:Extending Overrides扩展覆盖
【发布时间】:2018-08-30 12:36:00
【问题描述】:

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

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

【问题讨论】:

    标签: typescript overriding


    【解决方案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 方法声明接口时,扩展接口似乎会丢失调用签名超级接口。)

    【讨论】:

    • 很好的答案,谢谢。它解决了我眼前的问题。我会等到明天再将其标记为已接受的答案。
    猜你喜欢
    • 1970-01-01
    • 2015-08-10
    • 1970-01-01
    • 1970-01-01
    • 2015-10-04
    • 2017-06-28
    • 2014-01-29
    • 2019-11-28
    • 1970-01-01
    相关资源
    最近更新 更多