Typescript抽象方法重载
Typescript抽象方法重载
我想在抽象类中重载抽象方法,类似于这样:\n
abstract class Animal { public abstract communicate(sentence: string): void; public abstract communicate(notes: string[]): void; } class Human extends Animal { public communicate(sentence: string): void { // 做一些事情 } } class Bird extends Animal { public communicate(notes: string[]): void { // 做一些事情 } }
\n然而,TypeScript报错,指出我错误地扩展了基类(Animal)。\n我是否做错了什么?是否预期某些根据面向对象编程应该正常工作的东西?还是TypeScript不支持这样的写法?\n注意:参数的类型可以是完全不同的类型,不像这个例子中那样。
TypeScript中的抽象方法重载问题是由于在派生类中调用基类的抽象方法时,基类的抽象方法没有正确地被重载导致的。
在给定的代码中,`BaseComponent`是一个抽象类,它定义了一个抽象方法`doSomethingMoreButOptional`,并在构造函数中接收一个`valueName`参数。派生类`DerivedComponent`继承自`BaseComponent`,并重写了`doSomethingMoreButOptional`方法。
在派生类的构造函数中,首先调用了基类的构造函数`super('derived')`,然后调用了基类的`doSomething`方法`super.doSomething()`。然而,由于`doSomething`方法中使用了未定义的变量`valueName`,导致代码无法编译通过。
解决这个问题的方法是,在`doSomething`方法中使用`this.valueName`代替`valueName`,以引用派生类中的`valueName`属性。修改后的代码如下所示:
export abstract class BaseComponent { constructor(protected valueName: string) { } protected doSomethingMoreButOptional(config: IConfig): void { } protected doSomething() { if (this.valueName === 'derived') { this.doSomethingMoreButOptional(new Config()); } } } export class DerivedComponent extends BaseComponent { private _xxx: any[]; private _yyy: any[]; constructor() { super('derived'); super.doSomething(); } protected doSomethingMoreButOptional(config: IConfig): void { switch (config.ABC) { case 'xxx': config.options = this._xxx; break; case 'yyy': config.options = this._yyy; break; default: return; } } }
通过使用`this.valueName`来引用派生类中的属性,代码可以正确地编译和执行,解决了抽象方法重载的问题。
Typescript中的抽象方法重载问题出现的原因是,抽象类期望子类实现两个方法签名。这两个方法签名分别是:
public abstract communicate(sentence: string): void; public abstract communicate(notes: string[]): void;
可以通过以下方式来实现这两个方法签名:
class Human extends Animal { communicate(sentence: string); // optional overload signature communicate(notes: string[]); // optional overload signature communicate(sentenceOrNotes: string | string[]) { // Do stuff } }
最后一个方法签名是实现签名,它需要与需要实现的方法签名兼容。
关于子类的注意事项:
子类需要与基类兼容,这样当你这样做时...
const animal: Animal = new Bird();
...子类应该能够处理这两个调用:
animal.communicate("some sentence"); animal.communicate(["notes", "more notes"]);
在这种情况下,创建基于通信形式的单独接口(或使用混入)可能更合适:
interface CommunicatesWithSentences { communicate(sentence: string); } class Human extends Animal implements CommunicatesWithSentences { communicate(sentence: string) { // Do stuff } }
但是,这种方法的问题是,在子类中两个方法仍然是有效的。当然,我可以在使用不期望的方法时抛出错误,但我认为这不是一个很好的解决方法,因为它仍然可以被调用。有没有办法实现这个要求呢?
Typescript抽象方法重载的原因是为了强制子类实现父类中的所有重载方法,而不仅仅是其中一个。然而,如果想要让Bird和Human拥有不同的communicate()参数类型并且由编译器强制执行,可以使用带有类型约束的泛型类型参数。
在上述代码中,Animal类是一个抽象类,它有一个泛型类型参数C,该参数的约束是string或string[]类型。Animal类中有一个抽象方法communicate(communication: C),子类必须实现这个方法。
Human类继承Animal类,并指定泛型类型参数为string。它实现了communicate(sentence: string)方法。
Bird类也继承Animal类,并指定泛型类型参数为string[]。它实现了communicate(notes: string[])方法。
Bynar类继承Animal类,并指定泛型类型参数为boolean。由于boolean类型不满足约束string或string[],所以编译器会报错。
通过使用泛型类型参数,我们可以在编译时强制要求Bird和Human拥有不同的communicate()参数类型。
最后的代码示例展示了如何使用这些类。对于Human对象,我们可以传入一个字符串作为参数,但不能传入一个字符串数组;对于Bird对象,我们可以传入一个字符串数组作为参数,但不能传入一个字符串。
此外,值得一提的是,我们还可以利用TypeScript 2.3中的默认类型参数来简化代码。
至于参数个数不同的情况(例如1个和3个),在这种情况下,抽象方法重载是不适用的,因为抽象方法重载要求子类实现所有重载方法。如果参数个数不同,可以考虑使用可选参数或重载函数来实现不同个数的参数。