【问题标题】:Typescript child class function overloadingTypescript子类函数重载
【发布时间】:2016-10-27 18:42:44
【问题描述】:

我怎样才能在打字稿中实现类似于这种模式的东西?

class A {
    Init(param1: number) {
        // some code
    }
}

class B extends A {
    Init(param1: number, param2: string) {
        // some more code
    }
}

上面截取的代码看起来应该可以工作,但是仔细检查 How Typescript function overloading works 后发现抛出错误是有道理的:

TS2415: 'Class 'B' incorrectly extends base class 'A'. 
Types of property 'Init' are incompatible.

我知道构造函数允许这种行为,但我不能在这里使用构造函数,因为这些对象是为了提高内存效率而池化的。

我可以在 A 类中提供另一个 Init() 的定义:

class A {
    Init(param1: number, param2: string): void;
    Init(param1: number) {
        // some code
    }
}

但这并不理想,因为现在基类需要了解其所有派生类。

第三种选择是重命名 B 类中的 Init 方法,但这不仅会非常丑陋和令人困惑,而且会在基类中暴露 Init() 方法,这会导致难以检测的错误基类 Init() 被错误调用。

有没有什么方法可以实现这种模式而不存在上述方法的缺陷?

【问题讨论】:

    标签: inheritance typescript overloading


    【解决方案1】:

    对于想要扩展类型的人。 基于zlumer's answer,使用Intersection types

    interface ConsumerGroup {
      on(message: 'message'): void
      on(message: 'error'): void
    }
    
    interface ConsumerGroup2 {
      on(message: 'messageDecoded'): void;
      on(message: 'rawMessage'): void;
    }
    
    // Intersection types
    type ConsumerGroupEx = ConsumerGroup2 & ConsumerGroup;
    
    function newEvent(): ConsumerGroupEx {
      return "just for test" as unknown as ConsumerGroupEx;
    }
    
    const evt = newEvent();
    
    evt.on('messageDecoded'); // ok
    evt.on('message'); // ok
    evt.on('error'); // ok
    evt.on('notExist'); // compilation error
    
    

    【讨论】:

      【解决方案2】:

      TypeScript 抱怨方法不可互换:如果您执行以下操作会发生什么?

      let a:A = new A(); // a is of type A
      a.Init(1)
      a = new B(); // a is still of type A, even if it contains B inside
      a.Init(1) // second parameter is missing for B, but totally valid for A, will it explode?
      

      如果您不需要它们可互换,请修改B 的签名以符合A 的:

      class B extends A {
          Init(param1: number, param2?: string) { // param 2 is optional
              // some more code
          }
      }
      

      但是,您可能会发现自己需要创建一个具有完全不同的方法签名的类:

      class C extends A {
          Init(param1: string) { // param 1 is now string instead of number
              // some more code
          }
      }
      

      在这种情况下,添加满足当前类和基类调用的方法签名列表。

      class C extends A {
          Init(param1: number)
          Init(param1: string)
          Init(param1: number | string) { // param 1 is now of type number | string (you can also use <any>)
              if (typeof param1 === "string") { // param 1 is now guaranteed to be string
                  // some more code
              }
          }
      }
      

      这样A 类就不必知道任何派生类。作为权衡,您需要指定满足基类和子类方法调用的签名列表。

      【讨论】:

      • 感谢您的回复!如果可以避免的话,我宁愿不必进行运行时类型检查,但 Typescript 设法在其他任何地方都防止这种情况发生。
      • 不幸的是,如果您的子类的方法签名与基类的签名不匹配,您就不能没有运行时检查。看看我的第一个例子:有人可能会调用你的子类,就好像它实际上是基类一样
      • 啊,好吧,我想我现在明白了。也许我会让这两个类从一个抽象基类继承它们的其他共同属性。
      • a.Init(1) 应该明确调用 A 的 init 方法,因为这就是 OOP 的工作方式。带有 1 个参数的初始化签名由 A 继承,带有 2 个参数的初始化签名从 B 继承。
      猜你喜欢
      • 2012-10-24
      • 2021-04-23
      • 1970-01-01
      • 1970-01-01
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多