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:
this
not being able to reference the x1
in the TypeScript generated JS? Or, is that a bug in JS itself?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');
}
};