mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Emit defineProperty calls before param prop assignments
Note that I restricted this to --useDefineForClassFields is true.
Nothing changes when it's off. I think this is the correct fix for a
patch release.
However, in principal there's nothing wrong with moving parameter
property initialisation after property declaration initialisation. It
would be Extremely Bad and Wrong to rely on this working:
```ts
class C {
p = this.q // what is q?
constructor(public q: number) { }
}
```
But today it does, and probably somebody relies on it without knowing.
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -25,5 +25,8 @@ class A {
|
||||
|
||||
m() { }
|
||||
>m : () => void
|
||||
|
||||
constructor(public readonly y: number) { }
|
||||
>y : number
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,5 @@ class A {
|
||||
["computed"] = 13
|
||||
;[x] = 14
|
||||
m() { }
|
||||
constructor(public readonly y: number) { }
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user