-
Notifications
You must be signed in to change notification settings - Fork 9
Description
Let's start with the following variation of the original example.
enum E {
A = 1,
B = A + f(E).C,
C = 3,
}Applying the original desugaring to it, we get
let E = (() => {
let E = Object.create(null), A, B, C;
Object.defineProperty(E, "A", { value: A = 1 });
Object.defineProperty(E, "B", { value: B = A + f(E).C }); // NaN
Object.defineProperty(E, "C", { value: C = 3 });
Object.defineProperty(E, Symbol.iterator, {
value: function* () {
yield ["A", E.A];
yield ["B", E.B];
yield ["C", E.C];
}
});
Object.defineProperty(E, Symbol.toStringTag, { value: "E" });
Object.preventExtensions(E);
return E;
})();This has several problems:
f(E)is called beforeEis fully initialized, makingEavailable in its uninitialized state.- Even if
fwere the identity function, or if we had writtenA + E.Cinstead, we'd be getting the value of theCproperty before it is defined. Our normal expectation should be a thrown TDZ error. Instead, it silently initializesE.Bto1 + undefined, which isNaN.
By the simpler desugaring this issue proposes to use instead, it would desugar to
const E = (() => {
const A = 1;
const B = A + f(E).C; // TDZ
const C = 3;
return Object.freeze({ __proto__: null, A, B, C });
});which does produce the TDZ programmers should expect, rather than making E available to user code before it is fully initialized, and rather than mistakenly but silently initializing E.B to NaN.
Even the simpler example
enum E {
A = 1,
B = A + C,
C = 3,
}would still silently set B to NaN in the original desugaring, whereas it would throw a TDZ error in the desugaring we propose here.
The unproblematic way to express the intent of the original example
enum E {
A = 1,
B = 2,
C = A | B, // 3
}would work identically under both desugarings. In the simpler desugaring proposed here, it desugars to
const E = (() => {
const A = 1;
const B = 2
const C = A | B;
return Object.freeze({ __proto__: null, A, B, C });
});What about TypeScript interoperation?
Unproven claim
Any enum declaration that does not throw under the simpler desugaring would not throw under the original desugaring with no observable difference between the two.
We are indeed uncertain about this. Please try to find a counter-example.
Assuming this claim is true, and assuming the original desugaring accurately captures the semantics of the TypeScript compiler's desugaring, then adopting this simpler desugaring into the JS standard is well aligned with the goals of so-called "Erasable TypeScript" systems like ts-blank-space or Node's --experimental-strip-types option. "Erasable TypeScript" would be a subset of full TypeScript where programs that work in Erasable TypeScript also work in full TypeScript with the same meaning.
Erasable Typescript would thus be able to grow to include such enum declarations.