2

I have got this code:

var x = {
  x1: 5,
  x2: 7
};

var y = {
  ...x,
  _originalX2: x.x2,
  get x2() {
    console.log(this.x1);
    return 9;
  }
};
console.log(y.x2);

var z = {
  ...x,
  _originalX2: x.x2
};
Object.defineProperty(z, 'x2', {
  get: function() {
    console.log(this.x1);
    return 9;
  }
})
console.log(z.x2);

When I run this as JavaScript in the browser or in NodeJS, I get the output:

5
9
5
9

When I run the same code as TypeScript (see https://repl.it/repls/TornHomelyThing), I get the output:

undefined
9
5
9

I also see what JS TS generates from it @ https://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=22&pc=1#code/G4QwTgBAHhC8EG8oEYBcECsAaaAmdA7AL4DcAUGaJAJ5yJkQQB0LUODEA+gPZgCWAcz4A7EABsAGvihMoudowEBTAC54AFAEoEHRgGNuwgM7cxSpmO4D1KgBZ8js5JvKNGYVQFcwwiAE5XCCIyUjIDY1NzS2tqWVwXCioIAC86HUYWWQUuXkERcSlUGTkQ8gB5ACMAKyU9FSYAEyUAMxElAAUwbgAHJTAVanVknAByORGcBAhlFXRmz2E6vkMtejcIcJMzCysbe0cUBPWPFW9fAI4iIM0wwy2o3eS4hKA .

My question is two-fold:

  1. Is this to be considered a bug in TypeScript given that the same code in JS has different behavior?
  2. Is there a good reason for the this not being able to reference the x1 in the TypeScript generated JS? Or, is that a bug in JS itself?
Code Maniac
  • 37,143
  • 5
  • 39
  • 60
SBhojani
  • 499
  • 1
  • 4
  • 19
  • 1
    It indeed seems to be a bug in the typescript compiler. Worth mentioning: if you set typescript target to `es2018` or newer it works just fine. – elderapo Mar 26 '20 at 05:24
  • Yes it is check out the complied code from this typescript playground https://www.typescriptlang.org/play/index.html?ssl=1&ssc=1&pln=13&pc=11#code/G4QwTgBAHhC8EG8oEYBcECsAaaAmdA7AL4DcAUGaJAJ5yJkQQB0LUODEA+gPZgCWAcz4A7EABsAGvihMoudowEBTAC54AFAEoEHRgGNuwgM7cxSpmO4D1KgBZ8jOOw9nJN5RozCqArmGEQAJweEERkpGQGxqbmltbUsrjuFFQQAF50OowssgpcvIIi4lKoMnLh5ADyAEYAVkp6KkwAJkoAZiJKAApg3AAOSmAq1OppOADkcuM4CBDKKuhtPsKNfIZa9J4QUSZmFlY29kauyVveKn4BwRxEoZqRhruxB2mJyUA – Code Maniac Mar 26 '20 at 05:24

1 Answers1

3

Is this to be considered a bug in TypeScript given that the same code in JS has different behavior?

Yes, this definitely looks like a bug related to Typescript's transpilation of object spread syntax. The compiled TS is not doing the same thing as the JS.

In the first, you define a y object with a property x2, which is a getter:

var y = {
  ...x, 
  _originalX2:x.x2, 
  get x2(){

So referencing y.x2 later results in the getter being invoked. But in the compiled Typescript, we get:

var x = { x1: 5, x2: 7 };
var y = Object.assign(
  Object.assign({}, x),
  {
    _originalX2: x.x2,
    get x2() {
      console.log(this.x1);
    }
  }
);

This will result in the getter being invoked immediately - getters get invoked when in an object passed as the 2nd or more argument inside Object.assign. And when the getter is invoked, this refers to the second argument, this object here:

{
  _originalX2: x.x2,
  get x2() {
    console.log(this.x1);
  }
}

Which does not have an x1 property, thus it logs undefined.

The undefined is logged before the Object.assign finishes, before you get to the next line of console.log(y.x2);.

For a more minimal example:

var y = {
  ...{}, 
  get prop(){
    console.log('should not be invoked immediately');
  }
};

gets incorrectly transpiled to

var y = Object.assign({}, { get prop() {
        console.log('should not be invoked immediately');
    } });

If you make it so that Typescript doesn't transpile object spread syntax, by bumping the target past ES2017, it'll work. For example, with ESNext, the above gets transpiled to:

var y = {
    ...{},
    get prop() {
        console.log('should not be invoked immediately');
    }
};
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320