The proper method for assigning fields in a constructor within a subclass using TypeScript and Babel following TC39 guidelines

Encountered a peculiar issue where the values assigned to a field in a class were mysteriously overwritten with 'undefined' after running a constructor.

This problem seemed to be related to this GitHub issue, which is connected to this proposal.

Below is the code snippet that reproduces the issue:

While I can appreciate some of the rationale behind this decision, experts in Javascript and its intricacies have extensively debated it. I assumed their choice was more informed than one I could make myself.

However, the issue remains unsettling as it creates the illusion of functioning code until suddenly everything becomes undefined once the constructor completes its execution. So, my question is: what is the proper way to handle such situations? One workaround involves embedding something like myField: string = this.myField, but this code can be confusing for those who are not well-versed in the underlying mechanisms, leading most people to dismiss it as pointless.

I am grappling with finding the idiomatic approach for initializing fields within a constructor, and all my attempts thus far seem convoluted and potentially fall under the umbrella of an anti-pattern.

class Base {
    constructor(dto?){
        if(dto){
            this.updateFromDTO(dto);
        }
    }

    updateFromDTO(dto) : this{
        return this;
    }
}


class Extends extends Base {
    myField: string;

    updateFromDTO(dto) {
        super.updateFromDTO(dto);
        console.log('I was called');
        this.myField = "weee";
        console.log(this.myField);
        return this;
    }
}

console.log(new Extends("123"));//logs 'I was called', 'weee', then the extends object which has myField as undefined.

Babel configuration required to reproduce this behavior:

const DEVELOPMENT = 'production' !== process.env.NODE_ENV

module.exports = {
    presets: [
        ["@babel/preset-env", {useBuiltIns: "entry"}],
        "@babel/preset-react",
        "@babel/typescript"
    ],
    plugins: [
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-proposal-export-default-from",
        "@babel/plugin-syntax-dynamic-import",
        ["@babel/plugin-transform-runtime", {"regenerator": true}],
        "@babel/plugin-transform-classes",
    ],
}

Answer №1

One way to address the issue described in https://github.com/babel/babel/issues/9105 that is compatible with both tsc and babel involves declaration merging.

In your specific scenario:

  class Base {
    constructor(dto?: any) {
      if (dto) {
        this.updateFromDTO(dto);
      }
    }

    updateFromDTO(dto: any): this {
      return this;
    }
  }

  // utilizing declaration merging
  interface Extends {
    myField: string;
  }
  class Extends extends Base {
    updateFromDTO(dto: any) {
      super.updateFromDTO(dto);
      console.log("I was called");
      this.myField = "weee";
      console.log(this.myField);
      return this;
    }
  }

For a simpler example:

  interface A {}
  interface B {}

  class Base {
    prop: A | B | null = null;
  }

  class Sub1 extends Base {
    prop!: A | null; // narrowing the type
  }

  // using declaration merging
  interface Sub2 {
    prop: A | null; // narrowing the type
  }
  class Sub2 extends Base {
  }
  console.log("without declaration merging");
  console.log(`${new Sub1().prop}`); // tsc: null, babel: undefined
  console.log("with declaration merging");
  console.log(`${new Sub2().prop}`); // tsc: null, babel: null

The declaration merging approach informs typescript about the narrowed type without introducing a property to Sub2, resulting in @babel/typescript initializing it as undefined.

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

Uncomplicating Media Queries in Styled Components with the Power of React.js and Material-UI

My approach to handling media queries in Styled Components involves three distinct functions. One function handles min-width queries, another deals with max-width, and the third caters to both min-width and max-width scenarios. Let me walk you through eac ...

Am I utilizing window.setInterval correctly in this code, or am I potentially causing issues?

I have a search input with the class .searchinp that is provided by a third party and cannot be modified. However, I want to relocate the search results from the left side of the page to the right side. Currently, I am achieving this by using a setInterval ...

What is the best way to insert an image into the center of a Donut Chart using Chart.js?

Currently, I am utilizing Chart.js to visualize my data as shown in the following image: https://i.sstatic.net/5sEHW.png My goal is to incorporate an image in the center of the chart. https://i.sstatic.net/YDlOd.png Is there a way to insert an image in ...

What steps do I need to take for webpack to locate angular modules?

I'm currently in the process of setting up a basic application using Angular 1 alongside Typescript 2 and Webpack. Everything runs smoothly until I attempt to incorporate an external module, such as angular-ui-router. An error consistently arises ind ...

Error message: A circular structure is being converted to JSON in Node.js, resulting in

So here is the error message in full TypeError: Converting circular structure to JSON --> starting at object with constructor 'Socket' | property 'parser' -> object with constructor 'HTTPParser' --- prope ...

The unusual behavior of the :to attribute on @click in Vue

I have a specific element: Hashtag.vue: <template> <router-link :to="getTo" custom v-slot="{ navigate }"> <div role="link" @click="navigate"> {{text}}</div> </rout ...

The array is not being spliced in the DOM, however, it is being spliced in the console - Ionic 2+/Angular

My scenario involves a dynamic array filled with items and values. The goal is to remove an item from the view list when a user clicks a button on that particular item. I'm struggling to identify why this functionality isn't working as expected. ...

Start by executing the function and then proceed to upload a static file

Here is the code I am working with: var express = require('express'), app = express(); app.use(express.static(__dirname + '/static')); app.get('/', function(req, res) { //??? }); app.listen(80); I need to first ex ...

Displaying Material UI textboxes in multiple columns for dynamically created forms using ReactJs from an array

I am working with an API to retrieve a list of cars, and I have come up with the following code snippet to generate labels and textboxes for editing the car codes. {!carQuery.loading && carDefinitions?.map((car: ICarType, idx: number ...

Enhance the material-table component by incorporating dynamic information into the lookup property

I am currently facing challenges while attempting to dynamically add data to the lookup property in the Material-Table component. The lookup is structured as an object, and you can refer to its definition in the initial example provided here. However, it ...

Azure experiencing issue with MUI Datepicker where selected date is shifted by one day

I have developed a unique custom date selection component that utilizes Material UI and Formik. This component passes the selected date value to a parent form component in the following manner: import React from 'react'; import { useField } from ...

What might be causing the button switch case JavaScript functionality to not work properly?

In my Worklight project, I have a code snippet that is supposed to capture the value of a button that a user clicks on and then display that value in a label. However, when I run the project, none of the other functions I have defined seem to work. Strange ...

Is there a way to connect my host to localhost nodes for access?

Currently, I am immersed in a project based on Ionic. Instead of installing node.js directly onto my machine, I decided to experiment with DDEV, which is primarily known as a PHP development environment. Upon running my application through ionic serve -l, ...

What causes the failure of $event binding when using RowGroup tables with PrimeNG?

I have successfully implemented RowGroup to store 3 different tables, which is working great. However, I am facing an issue with the onRowSelect function not functioning properly. When I click on any row of the RowGroup tables, nothing happens. On another ...

The array containing numbers or undefined values cannot be assigned to an array containing only numbers

Currently facing an issue with TypeScript and types. I have an array of IDs obtained from checkboxes, which may also be empty. An example of values returned from the submit() function: const responseFromSubmit = { 1: { id: "1", value: "true" }, 2: ...

The maximize button mysteriously disappears in Ubuntu when using electron applications

I am encountering an issue with Ubuntu where the maximize screen button is not visible when compiling the Electron project. When I say "compile," I am referring to running electron build => ng build --configuration=dev && electron. My version of Electro ...

What is the best method to dynamically navigate through JSON data and save an object at a specific level?

Here is an example json structure for a menu: { "menu": [{ "name": "vegetation", "id": "1", "children": [ { "name": "landuse", "id": "1.1", "children": [ ...

Steps for transforming an array of arrays into a JSX element within a React component, where each nested array contains its own clipPath

The array_groups variable consists of an array containing multiple arrays. Each inner array holds the coordinates of regular circles that are closely spaced together. The intention is to clipPath these inner circle arrays as a whole. When the mouse hovers ...

Setting up state when a new window is launched

Let's consider a scenario where I already have a JWT token stored in cookies and the state contains a status flag for LoggedIn: true/false. The goal is to remain logged in when opening a new tab with the same Vue site. Below are the classes defined in ...

Initiate timer when mouse leaves; pause timer upon mouse hovering using JavaScript

I am currently working on creating a volume bar for a video. The idea is that when you click the volume button, a div will appear allowing you to control the volume level. As soon as you hover out of the div, a timer will start counting down from 7 and the ...