What is the proper way to invoke a child method after converting an object from a parent class to a child class?

When we have a subclass B that overrides a method from its superclass A in TypeScript, why does calling the method on an instance of A result in the parent class's implementation being called?

In TypeScript, consider a class called Drug with properties name and efficiency, along with a method updateEfficiency() that decreases the efficiency by 1:

class Drug {
  name: string;
  efficiency: number;

  constructor(name: string, efficiency: number) {
    this.name = name;
    this.efficiency = efficiency;
  }

  updateEfficiency() {
    this.efficiency -= 1;
  }
}

We also have a class named BottleOfWine which extends the Drug class and overrides the updateEfficiency() method to decrease efficiency by 2:

class BottleOfWine extends Drug {

  constructor(efficiency: number) {
    super("Old bottle of wine", efficiency);
  }

  updateEfficiency() {
    this.efficiency -= 2;
  }
}

Finally, there is a class called Inventory that contains an array of drugs and a method updateAll() to update the efficiency of all drugs:

class Inventory {

  drugs: Drug[];

  constructor(drugs: Drug[]) {
    this.drugs = drugs;
  }

  updateAll() {
    this.drugs.forEach((drug) => {
      switch (drug.name) {
        case "Old bottle of wine":
          // Here I expect to call the child method that decreases the efficiency by 2,
          // but the parent class is called instead
          (drug as BottleOfWine).updateEfficiency();
          break;
        case "Insulin vial":
          // Nothing to do
          break;
      }
    });
  }
}

However, when utilizing the updateAll() method, casting the drug object into a more specific (child) entity and invoking the method from the child class doesn't behave as expected. This can be seen in the following example:

const inventory = new Inventory([new Drug("Old bottle of wine", 10)])
inventory.updateAll();
console.log(inventory.drugs[0].efficiency); // Expected output: 8 (10 - 2), Actual output: 9 (10 - 1)

Any insights into this behavior?

Answer №1

Discussing my second comment...

Concerning the scenario presented by the original poster... When

new Drug("Old bottle of wine", 10)
is used, it creates a Drug object rather than a BottleOfWine instance. Therefore, achieving an efficiency value of 9 as observed by the OP is the expected outcome. The real question is, why does the OP intend to differentiate drug types based on a random name? Relying on a drug's name is not a dependable approach. The most reliable criterion for determining a drug item's type is through the class constructor function. Perhaps the OP should consider an alternative method for enabling users to create the appropriate drug type instances or provide a more thorough explanation of the entire situation.

An effective solution to managing how drug instances are created could involve eliminating sub-classing entirely. By utilizing only a single Drug class implementation, any confusion can be avoided. Additionally, one could modify the implementation to include an additional parameter that indicates the increase or decrease in a drug item's efficiency value...

const inventory = new Inventory([
  new Drug("Old bottle of wine", 10, -2),
  new Drug("Homegrown Weed", 20, -1),
]);

console.log({
  drugList: structuredClone(inventory.drugs),
});
inventory.updateAll();

console.log({
  drugList: structuredClone(inventory.drugs),
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
class Drug {

  constructor(name = '', efficiency = 0, efficiencyDelta = 0) {

    name = String(name);
    efficiency = Number(efficiency);
    efficiencyDelta = Number(efficiencyDelta);

    Object.assign(this, { name, efficiency, efficiencyDelta });
  }
  updateEfficiency() {

    this.efficiency += this.efficiencyDelta;
  }
}

class Inventory {

  constructor(drugs = []) {
    this.drugs = Array.from(drugs);
  }
  updateAll() {
    this.drugs.forEach(drug => drug.updateEfficiency())
  }
}
</script>

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

Is it possible to maintain the sidebar while eliminating the scrolling JavaScript?

Looking to recreate the image display style on Facebook where pictures appear in a lightbox format. The challenge I'm facing is figuring out how they manage to have the sidebar with no visible scroll bar inside... If you want to see this for yourself ...

An effective way to extract targeted information from an array of objects using Typescript

I'm having trouble extracting the IDs from the selected items in my PrimeNG DataTable. Despite searching on Google, I couldn't find much information about the error I'm encountering... ERROR in C:/Users/*****/Documents/Octopus/Octopus 2.0/s ...

Troubleshooting: ngAnimate max-height failing to apply to specific element

Having integrated ngAnimate into a project to enhance animations, I've encountered some unusual behavior with a specific element. Let me provide some context: our website features a shop with categories and products within those categories. I am usin ...

AngularJS promises are known for returning the object itself instead of just the resolved value

function getBusTimetable() { var waiting = $q.defer(); var busData = $http.get('https://bus.data.je/latest'); busData.success(function(response) { waiting.resolve(response); }); busData.error(function(error) { waiting.reject( ...

What is the best way to create a timer using JavaScript?

I'm looking for a reverse timer to track session timeout. I found a code on codepen that works as a clockwise timer, but my attempts to make it counterclockwise have failed. Can someone please suggest a solution? I want the timeout to be either 1 hour ...

Trouble updating values in Javascript objects

I'm having trouble understanding a problem I am experiencing. When I receive a Json object as a Websocket message from a hardware device, the property `uiAppMsg` is encoded in base64. After decoding it, I attempt to reassign it to the `uiAppMsg` prop ...

Utilizing a third-party npm package within an Angular 2 project

I have been trying to integrate the file-system npm library into my Angular 2 project by following these steps closely: https://medium.com/@s_eschweiler/using-external-libraries-with-angular-2-87e06db8e5d1#.1dx1fkiew Despite completing the process, I am e ...

Running a Jest test that triggers process.exit within an eternal setInterval loop with a latency of 0, all while utilizing Single

In the original project, there is a class that performs long-running tasks in separate processes on servers. These processes occasionally receive 'SIGINT' signals to stop, and I need to persist the state when this happens. To achieve this, I wrap ...

AngularJS - Refreshing Controller when Route Changes

Scenario app.controller('headerController', ['$scope', '$routeParams', function($scope, $routeParams) { $scope.data = $routeParams; }]); app.config(['$routeProvider', function ($routeProvider) { ...

Exploring Angular scope variables using Jasmine while making an ajax request

Can anyone provide guidance on testing Angular scope variables in a controller that is created within an ajax request? Here's the setup: app.controller('NewQuestionCtrl', function ($scope, Question) { loadJsonAndSetScopeVariables($scope ...

Encountered an error loading resource: server returned a 400 (Bad Request) status when using Angular with NodeJS

My Angular and NodeJS application with Express (v4.17.1) exposes a REST API like this: app.get('/myRestApi', function (req, res) { console.log('here you are ... '); res.end('success!\n'); }); The body of this API ...

The array is failing to pass through ajax requests

Here is the JavaScript code snippet for making an AJAX call. In this scenario, the code variable is used to store a C program code and then pass it on to the compiler.php page. function insert(){ var code = document.getElementById("file_cont").val ...

What is the reason behind AngularJS consistently selecting the first TD cell?

I'm a beginner in AngularJS and I'm testing my skills by creating a small game. Here's the table structure I have: <table class="board"> <h1>Table</h1> <input type="number" ng-model="val"><button ng-click="ctrl.f ...

How to access a custom filter in ng-repeat using AngularJS

I'm working on creating a filter to sort through the items displayed in a table. Specifically, I want to filter out items based on a certain property value that may change depending on user input. I have attempted the following approach and it seems t ...

Customize Date Display in Material-UI DataGrid

How do I change the date format in MUI DataGrid from mongo's format to moment.js? In addition, I would like to add a new field with an edit icon that, when clicked, will redirect to the edit page. Here is what my code looks like: const columns = [ ...

Troubleshooting the Checkbox Oncheck Functionality

Before checking out the following code snippet, I have a requirement. Whenever a specific checkbox (identified by id cfc1) is clicked, it should not show as checked. I have implemented the onCheck function for this purpose, but I'm struggling to fig ...

Rearrange the items in an array that contain different data types

After spending some time searching on SO, I did find some helpful information but I am still struggling to achieve the desired solution. My current issue involves a keypad with numbers 1 through 5. When a number key is selected, it gets added to an array n ...

javascript: verify the presence of uppercase and lowercase letters

Can the validation of at least 2 lowercase and 2 uppercase letters be implemented when checking the case? Below is the condition I am currently using. function HasMixedCase(passwd){ if(passwd.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)) return true ...

In what way can you reach an unfamiliar form within a controller?

I am working with multiple dynamically generated forms, each associated with a different model. In my controller, I need to iterate through all the errors within the forms. I assign form names based on the models. <form name="{{myForm}}" novalidate> ...

Embrace positive practices when working with nodejs

What is the best way to enhance the readability of my code by replacing the extensive use of if and else if statements when detecting different file extensions? Currently, I have over 30 else ifs in my actual code which makes it look cluttered. // Retrie ...