-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Description
Bug Report
π Search Terms
class static in:title
π Version & Regression Information
- This is the behavior in every version I tried (note: static blocks were introduced in TS 4.4)
β― Playground Link
Playground link with relevant code
π» Code
class Foo {
static { console.log(this, Foo) }
static x = () => { console.log(this, Foo) }
}
const oldFoo = Foo;
(Foo as any) = null;
oldFoo.x();π Actual behavior
If you click Run, the code that TypeScript generates prints something like this:
class Foo {} class Foo {}
class Foo {} null
The code that TypeScript generates looks like this:
"use strict";
var _a;
class Foo {
}
_a = Foo;
(() => {
console.log(_a, Foo);
})();
Foo.x = () => { console.log(_a, Foo); };
const oldFoo = Foo;
Foo = null;
oldFoo.x();π Expected behavior
If you run this code natively, it prints something like this:
class Foo {} class Foo {}
class Foo {} class Foo {}
I expected TypeScript to generate something like this instead:
"use strict";
var _a;
class Foo {
}
_a = Foo;
(() => {
console.log(_a, _a);
})();
_a.x = () => { console.log(_a, _a); };
const oldFoo = Foo;
Foo = null;
oldFoo.x();Class declarations in JavaScript generate an outer binding name, which turns out to be mutable (and can be reassigned). Separately, JavaScript also generates an immutable inner binding name that is only in scope within the class body. Referencing the class name within the class body gives the inner immutable binding name, and referencing the class name outside the class body gives the mutable outer binding name.
Presumably the _a that TypeScript is generating is this immutable inner binding name for code within the class body that needs to be moved out of the class body during syntax lowering. However, TypeScript isn't using the _a variable in all places where the immutable inner binding name should be referenced instead of the outer binding name. This leads to bugs in the case where the outer binding name is reassigned.
I discovered this bug in TypeScript because I'm trying to figure out how to handle this in esbuild, and as part of that I'm investigating how TypeScript transpiles code that does this.