How to model an abstract class and call its constructor?

I am trying to follow along with a book of C # design models by writing my code in TypeScript. This may be my first mistake, but I like to learn the language.

TypeScript does not support the abstract keyword for classes, so I'm trying to mimic it. Perhaps this is my second mistake.

Here is my interface and classes:

interface IEngine { getSize(): number; getTurbo(): boolean; } class AbstractEngine implements IEngine { constructor(private size: number, private turbo: boolean) { throw "Abstract class"; } public getSize(): number { return this.size; } public getTurbo(): boolean { return this.turbo; } public toString(): string { var funcNameRegex = /function (.{1,})\(/; var results = (funcNameRegex).exec(this.constructor.toString()); var className = (results && results.length > 1) ? results[1] : ''; return className + " (" + this.size + ")"; } } class StandardEngine extends AbstractEngine { constructor(size: number) { // not turbo charged super(size, false); } } 

When I try to create an instance of AbstractEngine using new AbstractEngine(1, true) I get the "Abstract class" error, as expected.

When I try to create an instance of StandardEngine using new StandardEngine(9000) I also get the "Abstract class" error.

Is there a way to model an abstract class in TypeScript, could it not be created, but still call super in a class that extends it? What about modeling abstract methods, can I protect them and still call the super method?

+6
source share
4 answers

I advise you not to do this. When the TypeScript compiler implements the abstract function mechanism, it's time to use it. But the hacks that run at runtime are incomprehensible and degrade performance.

Interfaces are the great power of TypeScript. They should be used in bulk.

Your example should be written as follows:

 interface Engine { getSize(): number; getTurbo(): boolean; } class StandardEngine implements Engine { constructor(private size: number, private turbo: boolean) { } public getSize(): number { return this.size; } public getTurbo(): boolean { return this.turbo; } } 

The simplest solution is often the best.

If you want to reuse code without a parent class, which then will definitely be used, the directory offers Mixins . Mixins are a way to master skills from several different objects.

Or, with the help of modules, you can save a private implementation (and, therefore, organize it at your discretion) and export only interfaces and factories. Example:

 module MyEngineModule { export interface Engine { getSize(): number; getTurbo(): boolean; } export interface StandardEngine extends Engine { } export function makeStandardEngine(size: number, turbo: boolean): StandardEngine { return new ImplStandardEngine(size, turbo); } // here classes are private and can inherit or use mixins… class ImplEngine { constructor(private size: number, private turbo: boolean) { } public getSize(): number { return this.size; } public getTurbo(): boolean { return this.turbo; } } class ImplStandardEngine extends ImplEngine implements StandardEngine { } } console.log(MyEngineModule.makeStandardEngine(123, true).getSize()); 
+1
source

TypeScript 1.6 is currently live and supports the abstract keyword.

 abstract class A { foo(): number { return this.bar(); } abstract bar(): number; } var a = new A(); // error, Cannot create an instance of the abstract class 'A' class B extends A { bar() { return 1; } } var b = new b(); // success, all abstracts are defined 
+5
source

When calling the StandardEngine constructor , you have a call to super(size, false) . This call to the base class is what generates the second "Abstract class" error.

To mimic the abstract base class that will be generated when instantiating, create the Init function, which is called from your derived class.

 class AbstractEngine implements IEngine { private _size: number; private _turbo: boolean; constructor() { throw "Abstract class"; } init(size:number, turbo: boolean) { this._size = size; this._turbo = turbo; } } class StandardEngine extends AbstractEngine { constructor(size: number) { // not turbo charged // do not call super here init(size, false); } } 
0
source

An alternative solution would be a custom property, which, if set indicates that the constructor is being called from a child class, is safe to continue. This is shown below:

 class AbstractEngine { safe; // IMPORTANT : do not initialize constructor(private size: number, private turbo: boolean) { if(!this.safe) throw "Abstract class"; // IMPORTANT } } class StandardEngine extends AbstractEngine { constructor(size: number) { this.safe = true; // IMPORTANT super(size, false); } } 
0
source

Source: https://habr.com/ru/post/972430/


All Articles