在扩展 class 中用不同的参数类型覆盖方法 - 打字稿

Override method with different argument types in extended class - Typescript

我想覆盖一个方法并将不同的参数类型传递给它:

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

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

然而这会产生打字稿错误:

Property 'myMethod' in type 'Child' is not assignable to the same property in base type 'Base'. Type '(myNumber: number) => undefined' is not assignable to type '(myString: string) => undefined'. Types of parameters 'myNumber' and 'myString' are incompatible. Type 'string' is not assignable to type 'number'.

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

没有办法做到这一点*。在 TypeScript 中,inheritance implies subtyping,所以当你不是它的有效子类型时,你不能继承基 class。

* 技术上不正确,但我不会提及它们,因为它们是你不应该做的粗俗黑客。

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

您可以轻松做到的是提供一个同时采用 stringnumber 的覆盖。这允许您的 class 仍然可以在任何需要基础 class 的地方使用。

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);
        }
    }
}

似乎有一种方法可以工作,即使对于 return 类型,至少对于使用临时 class.

的 TypeScript v 4.02

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

所以值得一提只是为了知识,但在代码审查中我不会接受这种 hack,因为 subclass 应该能够替换基础 class 而无需破坏功能。

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

只要创建第三个 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
    }