@babel/proposal-class-properties class field initialization override field value in some situation

Bug Report

  • I would like to work on a fix!

Current Behavior
@babel/proposal-class-properties generate field initialization code, which overrides the field values assigned dynamically in base class.
Although dynamically assign field value is not good, but sometimes it may write such code. For example: in base class, read a JSON file, and dynamically assign values for class members.

Input Code

class Base {
    constructor(obj: any = null) {
        if (obj != null) {
            console.log('in base');
            for (let key in obj) {
                this[key] = obj[key];
                console.log(`  this.${key}: ${this[key]}`);
            }
        }
    }
}

class Extended extends Base {
    id: number;
    name: string;

    constructor(obj: any = null) {
        super(obj);
        console.log('in extended');
        console.log(`  this.id: ${this.id}`);
        console.log(`  this.name: ${this.name}`);
    }
}

new Extended({id: 1, name: "test"});

Expected behavior/code
the correct output should be:

in base
  this.id: 1
  this.name: test
in extended
  this.id: 1
  this.name: test

but now it is:

in base
  this.id: 1
  this.name: test
in extended
  this.id: undefined
  this.name: undefined

targeting ES6, the transformed code for class Extended is:

class Extended extends Base {
  constructor(obj = null) {
    super(obj);
    this.id = void 0;
    this.name = void 0;
    console.log('in extended');
    console.log(`  this.id = ${this.id}`);
    console.log(`  this.name = ${this.name}`);
  }
}

the field initialization caused the problem.

Babel Configuration (babel.config.js, .babelrc, package.json#babel, cli command, .eslintrc)

  • Filename: .babelrc
{
  "presets": [
    "@babel/typescript"
  ],
  "plugins": [
    "@babel/proposal-class-properties",
    "@babel/proposal-object-rest-spread"
  ]
}

Possible Solution
Do not generate field initialization code when there is only a class field declaration without value initialization, especially in loose mode.

class Extended extends Base {
    public id: number;    // do not generate initialization code
    public name: string = 'test'; // generate initialization code
}

1 possible answer(s) on “@babel/proposal-class-properties class field initialization override field value in some situation

  1. Please use the declare syntax if you intend public id: number to be a type on declaration.

    class A {
      foo: string | void; // initialized to undefined
      declare bar: number; // type-only
    }

    Since it is introduced in recent TypeScript 3.7, it is behind an allowDeclareFields flag.

    {
      presets: [
        ["@babel/typescript", { "allowDeclareFields": true }]
      ]
    }

    In Babel 8 we will remove uninitialized type only declaration.