【问题标题】:Override method with different argument types in extended class - Typescript在扩展类中使用不同参数类型覆盖方法 - Typescript
【发布时间】:2018-06-06 20:57:39
【问题描述】:

我想重写一个方法并向它传递不同的参数类型:

class Base {
    public myMethod(myString: string): undefined {
        return;
    }
}

class Child extends Base {
    public myMethod(myNumber: number): undefined {
        return super.myMethod(String(myNumber));
    }
}

但这会产生打字稿错误:

'Child' 类型中的属性 'myMethod' 不能分配给相同的 基本类型“Base”中的属性。输入'(myNumber:number)=> undefined' 不可分配给类型 '(myString: string) => undefined'。 参数“myNumber”和“myString”的类型不兼容。 类型“字符串”不可分配给类型“数字”。

有没有办法在不产生打字稿错误的情况下做到这一点?

【问题讨论】:

  • 你不能,也不应该;你违反了父母的合同和 Liskov 替换原则。
  • 你能解释一下你想要完成什么吗?这可能是那些XY situations 之一,您需要解决的问题有一个解决方案,而不是尝试覆盖具有不兼容签名的方法。
  • @jcalz 这只是一种解决方案,其他解决方案仍然适用于我的问题。我只是想知道这是否可行,因为我自己没有找到关于此的文档(但所有示例都使用了这个 Liskov 替换原则)

标签: typescript inheritance class-method


【解决方案1】:

没有办法做到这一点*。在 TypeScript 中,inheritance implies subtyping,因此您不能从基类继承而不是它的有效子类型。

* 从技术上讲不正确,但我不会提及它们,因为它们是你不应该做的严重黑客行为。

【讨论】:

    【解决方案2】:

    正如其他人所说,这不是一个好主意,因为您打破了 Liskov 替换。

    您可以轻松地提供一个覆盖stringnumber。这使您的类仍然可以在任何需要基类的地方使用。

    class Base {
         public myMethod(myString: string): undefined {
             return;
         }
     }
    
    class Child extends Base {
        public myMethod(myNumberOrString: number | string): undefined {
            if (typeof myNumberOrString === 'number') {
                return super.myMethod(String(myNumberOrString));
            } else {
                return super.myMethod(myNumberOrString);
            }
        }
    }
    

    【讨论】:

    • 任何方式如何使它也适用于返回类型,而不仅仅是参数?
    • @sKopheK 并非如此,对于参数,您可以摆脱它,因为派生类是处理新参数类型的类。对于返回类型,如果您的派生类返回一个数字,并且您在预期基类的地方使用派生类(您应该能够),则使用基类的代码无法处理意外的返回类型。您可以对类型进行一些手术并创建与基类不兼容的派生类。但是作为一个不同的问题来问,因为这个答案对 cme​​ts 来说有点长
    【解决方案3】:

    似乎有一种方法有效,即使对于返回类型,至少使用临时类的 TypeScript v 4.02。

    但这违反了 Liskov 替换原则,因为它改变了返回类型并且没有处理参数是字符串的情况。

    所以值得一提的是为了知识,但在代码审查中我不会接受这种 hack,因为子类应该能够在不破坏功能的情况下替换基类。

    class Base {
        public myMethod(myString: string): string {
            return myString + myString;
        }
    }
    
    // weaken
    // inspired by comment here: https://github.com/microsoft/TypeScript/issues/3402#issuecomment-385975990
    class Interim extends Base {
        public myMethod(x: any): any { return super.myMethod(x); }
    }
    
    class Child extends Interim {
        public myMethod(myNumber: number): number {
            return myNumber * myNumber;
        }
    }
    
    // we can have a helper/utility for this
    function weaken(klass: { new(...args: any[]): any; }, key: string) {
        return class extends klass {
            [key](...args: any[]) { super[key](...args); }
        }
    }
    
    
    class ChildOther extends weaken(Base, 'myMethod') {
        public myMethod(myNumber: number): number {
            return myNumber * myNumber;
        }
    }
    
    console.log((new Child()) instanceof Base); // true
    console.log((new ChildOther()) instanceof Base); // true
    console.log(new Base().myMethod('str')); // strstr
    console.log(new Child().myMethod(3)); // 9
    console.log(new ChildOther().myMethod(3)); // 9

    【讨论】:

    • 感谢分享,效果如预期。除了记录由于某些情况而无法重构的现有代码之外,我很好奇您是否看到任何用例。
    • 虽然我认为它回答了我的问题并且我赞成它,但我会留下已接受的“不要这样做......但这是可能的”答案。真正需要解决方法的人,稍微滚动一下还是会找到的。
    【解决方案4】:

    只要您创建第三个BaseClass 并从中扩展您的两个类,您就可以轻松地做到这一点。只需确保仅在扩展类中定义具有不同参数的函数(因此它们不会重叠)。这是它的一个简单示例(我的不同功能称为 specialFunction):

        class BaseClass { // FirstClass and SecondClass both extend this class
            constructor() {
                this.name = ""
            }
            name: string
        }
    
        class FirstClass extends BaseClass {
            specialFunction(name: string) { // <- this only has one parameter
                this.name = name
            }
        }
    
        class SecondClass extends BaseClass {
            constructor() {
                super()
                this.num = 0
            }
            specialFunction(name: string, num: number) { // <- this has two parameters
                this.name = name
                this.num = num
            }
            num: number
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-30
      相关资源
      最近更新 更多