在扩展 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 替换。
您可以轻松做到的是提供一个同时采用 string
和 number
的覆盖。这允许您的 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
}
我想覆盖一个方法并将不同的参数类型传递给它:
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 替换。
您可以轻松做到的是提供一个同时采用 string
和 number
的覆盖。这允许您的 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
}