What is the reason behind Typescript executing the abstract class before anything else?

I'm currently facing a challenge solving an abstract class problem with Typescript. Let me explain what I am trying to accomplish.

There is a class named Sword that extends Weapon. Each Weapon must have certain properties like the damage, but since each weapon type inflicts different levels of damage (for example, a sword may deal 1 damage while a bow deals 2 damage), I need to define specific properties in the Sword class. Here's how my script looks:

abstract class Weapon
{
    protected abstract damage: number;
    constructor() {
        alert(this.damage);
    }

    showDamage() {
        alert(this.damage);
    }
}

class Sword extends Weapon implements WeaponInterface {
    protected damage: number = 999;

    constructor() {
        super();
    }
}

const sword = new Sword;
sword.showDamage();

When running this script on

http://www.typescriptlang.org/play/
, I receive two messages:

undefined
999

I'm unsure why the Weapon.constructor gets executed first. This seems to defeat the purpose of declaring an abstract value. If I have to use super(this.damage) to pass it into the Weapon class, there doesn't seem to be a need for protected abstract damage.

If I can't even establish basic inheritance in Typescript, why does it offer support for abstract classes? It forces me to do new Weapon(new Sword), making it impossible to typehint a SwordInterface on other classes like Inventory.

class Inventory
{
    // Assuming we are equipped with a "Shield," we can only equip items of type "Sword"
    addSword(sword: SwordInterface): void {

    }
}

As someone new to compiled languages and Typescript, I'm seeking guidance on the proper way to achieve this without resorting to passing class properties into the super() call.

I want to maintain inheritance and interfaces without any disruptions.

Answer №1

When it comes to Typescript, property initialization in the class body is not treated in a special way; it is done as part of the class constructor. However, one could envision a language where this assignment happens very early, before any constructor is executed. Typescript does not follow this approach, likely because it may not be the most straightforward and coherent method.

class Sword extends Weapon implements WeaponInterface {
    protected damage: number = 999;

Therefore, you need to take matters into your own hands. One approach to accomplish your desired outcome is by splitting your code in constructors into two parts: one for initializing variables and the other for executing the remaining logic. This strategy is sometimes referred to as two-phase initialization:

abstract class Weapon
{
    protected abstract damage: number;

    // NOTE: abstract properties must be initialized by subclasses
    //     in initialize() because they are used here in Weapon class constructor
    protected initialize(): void { }

    constructor() {
        this.initialize();
        alert(this.damage);
    }

    showDamage() {
        alert(this.damage);
    }
}

class Sword extends Weapon  {
    protected damage: number;

    protected initialize(): void {
        super.initialize();
        this.damage = 999;
    }

    constructor() {
        super();
    }
}

const sword = new Sword;
sword.showDamage(); // shows 999 twice

Answer №2

It's a given that base class constructors will always run before derived class constructors. Can you imagine any other scenario?

class Base {
  damage = 12;
}

class Derived extends Base {
  constructor() {
    // Do you expect this to output 'undefined'?
    console.log(this.damage);
  }
}

Answer №3

Prior to setting the damage property, the base constructor is executed first in this scenario. Here is the constructor for the Sword class:

function Sword() {
    var _this = _super.call(this) || this;
    _this.damage = 999;
    return _this;
}

An alternative approach would be to pass the damage as a parameter to the base constructor:

abstract class Weapon {

    protected damage: number;

    constructor(damage: number) {
        this.damage = damage;
        alert(this.damage);
    }

    showDamage() { /*...*/ }
}

class Sword extends Weapon  {

    constructor() {
        super(999);
    }
}

A more concise version could look like this:

abstract class Weapon {
    constructor(protected damage: number) {
        alert(this.damage);
    }

    showDamage() { /* ... */ }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing TypeScript with Svelte Components

I've been struggling to implement <svelte:component /> with Typescript without success. Here's my current attempt: Presentation.svelte <script lang="ts"> export let slides; </script> {#each slides as slide} & ...

Find keys in an array based on a specified value

I need to retrieve an array of keys from an object that match a specified value ...

"Implementing a Filter for Selecting Multiple Options in Ionic Framework

I need help with filtering books in an online library project using a modal page. The modal has 3 input fields for title, author, and year. How can I filter the books based on these inputs? Here is a snippet of my modal.html code: <ion-content pa ...

Resolve cyclic dependency caused by utilizing the useFactory parameter

I am working with an injectable service that utilizes the useFactory attribute to determine whether it should be injected or if an implemented type should be used instead. import { Injectable } from '@angular/core'; import { Router } from ' ...

What is the best way to send my Array containing Objects to the reducer using dispatch in redux?

I'm currently facing an issue where I can only pass one array item at a time through my dispatch, but I need to pass the entire array of objects. Despite having everything set up with a single array item and being able to map and display the data in t ...

A TypeScript object with user-defined keys

I have a question about utilizing TypeScript records with a custom Type as keys. Essentially, I have a specific type (a limited set of strings) that I want to use as keys for my record. My goal is to start with an empty initialization of this record. type ...

Attempting to create a login feature using phpMyAdmin in Ionic framework

Currently, I am in the process of developing a login feature for my mobile application using Ionic. I am facing some difficulties with sending data from Ionic to PHP and I can't seem to figure out what the issue is. This is how the HTML form looks li ...

Tips for receiving a linter/compiler warning when comparing a function without its call being made?

Often, I find myself making a common mistake when writing TypeScript code: class Foo { constructor() { } public get isFoo(): boolean { return true; } // getter public isBar(): boolean { return false; } // normal function } let foo = new Foo(); if ( ...

How can I simulate a callback function that was not tested?

Currently experimenting with the method below: startScriptLoad(): void { const documentDefaultView = this.getDocumentDefaultView(); if (documentDefaultView) { const twitterData: ICourseContentElementEmbedTweetWidgetData = this.getTwitterWid ...

Achieving the functionality of making only one list item in the navbar bolded upon being clicked using React and Typescript logic

Currently, in my navigation bar, I am attempting to make only the active or clicked list item appear bold when clicked. At the moment, I can successfully achieve this effect; however, when I click on other list items, they also become bolded, while the ori ...

Applying specific data types to object properties for precise value identification in Typescript

I've been working on creating a dynamic settings menu in TypeScript using the following data: const userSettings = { testToggle: { title: "Toggle me", type: "toggle", value: false, }, testDropdow ...

Angular 8 delivers an observable as a result following a series of asynchronous requests

I am working on a simple function that executes 3 asynchronous functions in sequence: fetchData() { this.fetchUsers('2') .pipe( flatMap((data: any) => { return this.fetchPosts(data.id); }), fl ...

Commit to calculating the total sum of each element using AngularJS

Trying to implement a like counter using Facebook's GRAPH API. I have a list of object IDs and for each ID, I make an API call to retrieve the number of likes and calculate a total. The issue arises as the API call returns a promise, causing only one ...

I am searching for answers to solve issues related to a JSON file

I am currently working on a tool that searches for matches in an input field by comparing the keywords entered by the user with a JSON. During my testing phase, I focused on using a single API that provides information about different countries and fortun ...

Is it possible to enter NaN in Vue3?

Is there a way to handle NaN values and keep a field blank instead when calculating margins with a formula? https://i.stack.imgur.com/JvIRQ.png Template <form> <div class="row"> <div class="mb-3 col-sm ...

Capture a screenshot with Puppeteer at a random URL stop

I am facing an issue with my service nodejs running on Ubuntu, where I use puppeteer to capture screenshots of pages. However, the method page.screenshot({fullPage: true, type: 'jpeg'}) sometimes fails on random URLs without displaying any errors ...

Secure method of utilizing key remapped combined type of functions

Imagine having a union type called Action, which is discriminated on a single field @type, defined as follows: interface Sum { '@type': 'sum' a: number b: number } interface Square { '@type': 'square&apos ...

Manipulate MySQL data in Node.js by storing it in a variable

Struggling to grasp the concepts of nodeJS/typescript and how to effectively save database query results into variables for return. Seeking assistance to solve the problem faced: Here is a method snippet that needs help: public getAllProducts(): ProductA ...

Is there a way to extract the HTMLElement[] type from the DOM?

In my TypeScript project, I am trying to gather all the top-level elements of a page using the code snippet below: const getHTMLElement() : HTMLElement[] { const doc = document.body.children; const list : HTMLElement[] = []; for (let c of Array.f ...

How to refresh a page manually in Angular 2

How can I have a page in Angular reload only once when a user visits it? This is my attempt: In the homepage component, I added the following code: export class HomepageComponent implements OnInit { constructor() { } ngOnInit() { location.relo ...