diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index b3e59860888..265300adbd9 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -10,9 +10,9 @@ namespace ts { /** * Transforms ECMAScript Class Syntax. * TypeScript parameter property syntax is transformed in the TypeScript transformer. - * For now, this transforms public field declarations using TypeScript class semantics - * (where the declarations get elided and initializers are transformed as assignments in the constructor). - * Eventually, this transform will change to the ECMAScript semantics (with Object.defineProperty). + * For now, this transforms public field declarations using TypeScript class semantics, + * where declarations are elided and initializers are transformed as assignments in the constructor. + * When --useDefineForClassFields is on, this transforms to ECMAScript semantics, with Object.defineProperty. */ export function transformClassFields(context: TransformationContext) { const { @@ -294,7 +294,8 @@ namespace ts { } function transformConstructorBody(node: ClassDeclaration | ClassExpression, constructor: ConstructorDeclaration | undefined, isDerivedClass: boolean) { - const properties = getProperties(node, /*requireInitializer*/ !context.getCompilerOptions().useDefineForClassFields, /*isStatic*/ false); + const useDefineForClassFields = context.getCompilerOptions().useDefineForClassFields; + const properties = getProperties(node, /*requireInitializer*/ !useDefineForClassFields, /*isStatic*/ false); // Only generate synthetic constructor when there are property initializers to move. if (!constructor && !some(properties)) { @@ -325,6 +326,9 @@ namespace ts { if (constructor) { indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements, visitor); } + if (useDefineForClassFields) { + addPropertyStatements(statements, properties, createThis()); + } // Add the property initializers. Transforms this: // @@ -336,7 +340,7 @@ namespace ts { // this.x = 1; // } // - if (constructor && constructor.body) { + if (constructor?.body) { let parameterPropertyDeclarationCount = 0; for (let i = indexOfFirstStatement; i < constructor.body.statements.length; i++) { if (isParameterPropertyDeclaration(getOriginalNode(constructor.body.statements[i]), constructor)) { @@ -351,7 +355,9 @@ namespace ts { indexOfFirstStatement += parameterPropertyDeclarationCount; } } - addPropertyStatements(statements, properties, createThis()); + if (!useDefineForClassFields) { + addPropertyStatements(statements, properties, createThis()); + } // Add existing statements, skipping the initial super call. if (constructor) { diff --git a/tests/baselines/reference/definePropertyES5.js b/tests/baselines/reference/definePropertyES5.js index a0bc4a64db4..1d1ec488cdc 100644 --- a/tests/baselines/reference/definePropertyES5.js +++ b/tests/baselines/reference/definePropertyES5.js @@ -6,6 +6,7 @@ class A { ["computed"] = 13 ;[x] = 14 m() { } + constructor(public readonly y: number) { } } @@ -13,7 +14,13 @@ class A { var _a; var x = "p"; var A = /** @class */ (function () { - function A() { + function A(y) { + Object.defineProperty(this, "y", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); Object.defineProperty(this, "a", { enumerable: true, configurable: true, @@ -38,6 +45,7 @@ var A = /** @class */ (function () { writable: true, value: 14 }); + this.y = y; } Object.defineProperty(A.prototype, "m", { enumerable: false, diff --git a/tests/baselines/reference/definePropertyES5.symbols b/tests/baselines/reference/definePropertyES5.symbols index f9afa028687..4fad0556075 100644 --- a/tests/baselines/reference/definePropertyES5.symbols +++ b/tests/baselines/reference/definePropertyES5.symbols @@ -21,5 +21,8 @@ class A { m() { } >m : Symbol(A.m, Decl(definePropertyES5.ts, 5, 13)) + + constructor(public readonly y: number) { } +>y : Symbol(A.y, Decl(definePropertyES5.ts, 7, 16)) } diff --git a/tests/baselines/reference/definePropertyES5.types b/tests/baselines/reference/definePropertyES5.types index 3f82787c13a..6beb7516bcd 100644 --- a/tests/baselines/reference/definePropertyES5.types +++ b/tests/baselines/reference/definePropertyES5.types @@ -25,5 +25,8 @@ class A { m() { } >m : () => void + + constructor(public readonly y: number) { } +>y : number } diff --git a/tests/baselines/reference/definePropertyESNext.js b/tests/baselines/reference/definePropertyESNext.js new file mode 100644 index 00000000000..56c7ce36f68 --- /dev/null +++ b/tests/baselines/reference/definePropertyESNext.js @@ -0,0 +1,43 @@ +//// [definePropertyESNext.ts] +var x: "p" = "p" +class A { + a = 12 + b + ["computed"] = 13 + ;[x] = 14 + m() { } + constructor(public readonly y: number) { } +} +class B { +} +class C extends B { + z = 1 + constructor(public ka: number) { + super() + } +} + + +//// [definePropertyESNext.js] +var x = "p"; +class A { + y; + a = 12; + b; + ["computed"] = 13; + [x] = 14; + m() { } + constructor(y) { + this.y = y; + } +} +class B { +} +class C extends B { + ka; + z = 1; + constructor(ka) { + super(); + this.ka = ka; + } +} diff --git a/tests/baselines/reference/definePropertyESNext.symbols b/tests/baselines/reference/definePropertyESNext.symbols new file mode 100644 index 00000000000..adc8913c085 --- /dev/null +++ b/tests/baselines/reference/definePropertyESNext.symbols @@ -0,0 +1,45 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/definePropertyESNext.ts === +var x: "p" = "p" +>x : Symbol(x, Decl(definePropertyESNext.ts, 0, 3)) + +class A { +>A : Symbol(A, Decl(definePropertyESNext.ts, 0, 16)) + + a = 12 +>a : Symbol(A.a, Decl(definePropertyESNext.ts, 1, 9)) + + b +>b : Symbol(A.b, Decl(definePropertyESNext.ts, 2, 10)) + + ["computed"] = 13 +>["computed"] : Symbol(A["computed"], Decl(definePropertyESNext.ts, 3, 5)) +>"computed" : Symbol(A["computed"], Decl(definePropertyESNext.ts, 3, 5)) + + ;[x] = 14 +>[x] : Symbol(A[x], Decl(definePropertyESNext.ts, 5, 5)) +>x : Symbol(x, Decl(definePropertyESNext.ts, 0, 3)) + + m() { } +>m : Symbol(A.m, Decl(definePropertyESNext.ts, 5, 13)) + + constructor(public readonly y: number) { } +>y : Symbol(A.y, Decl(definePropertyESNext.ts, 7, 16)) +} +class B { +>B : Symbol(B, Decl(definePropertyESNext.ts, 8, 1)) +} +class C extends B { +>C : Symbol(C, Decl(definePropertyESNext.ts, 10, 1)) +>B : Symbol(B, Decl(definePropertyESNext.ts, 8, 1)) + + z = 1 +>z : Symbol(C.z, Decl(definePropertyESNext.ts, 11, 19)) + + constructor(public ka: number) { +>ka : Symbol(C.ka, Decl(definePropertyESNext.ts, 13, 16)) + + super() +>super : Symbol(B, Decl(definePropertyESNext.ts, 8, 1)) + } +} + diff --git a/tests/baselines/reference/definePropertyESNext.types b/tests/baselines/reference/definePropertyESNext.types new file mode 100644 index 00000000000..1473a5fc1e6 --- /dev/null +++ b/tests/baselines/reference/definePropertyESNext.types @@ -0,0 +1,51 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/definePropertyESNext.ts === +var x: "p" = "p" +>x : "p" +>"p" : "p" + +class A { +>A : A + + a = 12 +>a : number +>12 : 12 + + b +>b : any + + ["computed"] = 13 +>["computed"] : number +>"computed" : "computed" +>13 : 13 + + ;[x] = 14 +>[x] : number +>x : "p" +>14 : 14 + + m() { } +>m : () => void + + constructor(public readonly y: number) { } +>y : number +} +class B { +>B : B +} +class C extends B { +>C : C +>B : B + + z = 1 +>z : number +>1 : 1 + + constructor(public ka: number) { +>ka : number + + super() +>super() : void +>super : typeof B + } +} + diff --git a/tests/cases/conformance/classes/propertyMemberDeclarations/definePropertyES5.ts b/tests/cases/conformance/classes/propertyMemberDeclarations/definePropertyES5.ts index 73e1ee515d1..f470e8c28d4 100644 --- a/tests/cases/conformance/classes/propertyMemberDeclarations/definePropertyES5.ts +++ b/tests/cases/conformance/classes/propertyMemberDeclarations/definePropertyES5.ts @@ -7,4 +7,5 @@ class A { ["computed"] = 13 ;[x] = 14 m() { } + constructor(public readonly y: number) { } } diff --git a/tests/cases/conformance/classes/propertyMemberDeclarations/definePropertyESNext.ts b/tests/cases/conformance/classes/propertyMemberDeclarations/definePropertyESNext.ts new file mode 100644 index 00000000000..f95ad06c772 --- /dev/null +++ b/tests/cases/conformance/classes/propertyMemberDeclarations/definePropertyESNext.ts @@ -0,0 +1,19 @@ +// @target: esnext +// @useDefineForClassFields: true +var x: "p" = "p" +class A { + a = 12 + b + ["computed"] = 13 + ;[x] = 14 + m() { } + constructor(public readonly y: number) { } +} +class B { +} +class C extends B { + z = 1 + constructor(public ka: number) { + super() + } +}