Skip to content

Commit 273a567

Browse files
authored
Fix handling of prologue statements when there are parameter property declarations (#48775)
1 parent ce487e4 commit 273a567

5 files changed

+289
-14
lines changed

src/compiler/transformers/classFields.ts

+16-14
Original file line numberDiff line numberDiff line change
@@ -1294,7 +1294,7 @@ namespace ts {
12941294
resumeLexicalEnvironment();
12951295

12961296
const needsSyntheticConstructor = !constructor && isDerivedClass;
1297-
let indexOfFirstStatementAfterSuper = 0;
1297+
let indexOfFirstStatementAfterSuperAndPrologue = 0;
12981298
let prologueStatementCount = 0;
12991299
let superStatementIndex = -1;
13001300
let statements: Statement[] = [];
@@ -1305,13 +1305,16 @@ namespace ts {
13051305

13061306
// If there was a super call, visit existing statements up to and including it
13071307
if (superStatementIndex >= 0) {
1308-
indexOfFirstStatementAfterSuper = superStatementIndex + 1;
1308+
indexOfFirstStatementAfterSuperAndPrologue = superStatementIndex + 1;
13091309
statements = [
13101310
...statements.slice(0, prologueStatementCount),
1311-
...visitNodes(constructor.body.statements, visitor, isStatement, prologueStatementCount, indexOfFirstStatementAfterSuper - prologueStatementCount),
1311+
...visitNodes(constructor.body.statements, visitor, isStatement, prologueStatementCount, indexOfFirstStatementAfterSuperAndPrologue - prologueStatementCount),
13121312
...statements.slice(prologueStatementCount),
13131313
];
13141314
}
1315+
else if (prologueStatementCount >= 0) {
1316+
indexOfFirstStatementAfterSuperAndPrologue = prologueStatementCount;
1317+
}
13151318
}
13161319

13171320
if (needsSyntheticConstructor) {
@@ -1354,26 +1357,25 @@ namespace ts {
13541357
}
13551358
}
13561359
if (parameterPropertyDeclarationCount > 0) {
1357-
const parameterProperties = visitNodes(constructor.body.statements, visitor, isStatement, indexOfFirstStatementAfterSuper, parameterPropertyDeclarationCount);
1360+
const parameterProperties = visitNodes(constructor.body.statements, visitor, isStatement, indexOfFirstStatementAfterSuperAndPrologue, parameterPropertyDeclarationCount);
13581361

13591362
// If there was a super() call found, add parameter properties immediately after it
13601363
if (superStatementIndex >= 0) {
13611364
addRange(statements, parameterProperties);
13621365
}
1363-
// If a synthetic super() call was added, add them just after it
1364-
else if (needsSyntheticConstructor) {
1366+
else {
1367+
// Add add parameter properties to the top of the constructor after the prologue
1368+
let superAndPrologueStatementCount = prologueStatementCount;
1369+
// If a synthetic super() call was added, need to account for that
1370+
if (needsSyntheticConstructor) superAndPrologueStatementCount++;
13651371
statements = [
1366-
statements[0],
1372+
...statements.slice(0, superAndPrologueStatementCount),
13671373
...parameterProperties,
1368-
...statements.slice(1),
1374+
...statements.slice(superAndPrologueStatementCount),
13691375
];
13701376
}
1371-
// Since there wasn't a super() call, add them to the top of the constructor
1372-
else {
1373-
statements = [...parameterProperties, ...statements];
1374-
}
13751377

1376-
indexOfFirstStatementAfterSuper += parameterPropertyDeclarationCount;
1378+
indexOfFirstStatementAfterSuperAndPrologue += parameterPropertyDeclarationCount;
13771379
}
13781380
}
13791381
}
@@ -1385,7 +1387,7 @@ namespace ts {
13851387

13861388
// Add existing statements after the initial prologues and super call
13871389
if (constructor) {
1388-
addRange(statements, visitNodes(constructor.body!.statements, visitBodyStatement, isStatement, indexOfFirstStatementAfterSuper + prologueStatementCount));
1390+
addRange(statements, visitNodes(constructor.body!.statements, visitBodyStatement, isStatement, indexOfFirstStatementAfterSuperAndPrologue));
13891391
}
13901392

13911393
statements = factory.mergeLexicalEnvironment(statements, endLexicalEnvironment());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//// [constructorWithParameterPropertiesAndPrivateFields.es2015.ts]
2+
// https://github.com/microsoft/TypeScript/issues/48771
3+
4+
class A {
5+
readonly #privateField: string;
6+
7+
constructor(arg: { key: string }, public exposedField: number) {
8+
({ key: this.#privateField } = arg);
9+
}
10+
11+
log() {
12+
console.log(this.#privateField);
13+
console.log(this.exposedField);
14+
}
15+
}
16+
17+
class B {
18+
readonly #privateField: string;
19+
20+
constructor(arg: { key: string }, public exposedField: number) {
21+
"prologue";
22+
({ key: this.#privateField } = arg);
23+
}
24+
25+
log() {
26+
console.log(this.#privateField);
27+
console.log(this.exposedField);
28+
}
29+
}
30+
31+
32+
//// [constructorWithParameterPropertiesAndPrivateFields.es2015.js]
33+
// https://github.com/microsoft/TypeScript/issues/48771
34+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
35+
if (kind === "m") throw new TypeError("Private method is not writable");
36+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
37+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
38+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
39+
};
40+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
41+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
42+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
43+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
44+
};
45+
var _A_privateField, _B_privateField;
46+
class A {
47+
constructor(arg, exposedField) {
48+
var _a;
49+
this.exposedField = exposedField;
50+
_A_privateField.set(this, void 0);
51+
(_a = this, { key: ({ set value(_b) { __classPrivateFieldSet(_a, _A_privateField, _b, "f"); } }).value } = arg);
52+
}
53+
log() {
54+
console.log(__classPrivateFieldGet(this, _A_privateField, "f"));
55+
console.log(this.exposedField);
56+
}
57+
}
58+
_A_privateField = new WeakMap();
59+
class B {
60+
constructor(arg, exposedField) {
61+
"prologue";
62+
var _a;
63+
this.exposedField = exposedField;
64+
_B_privateField.set(this, void 0);
65+
(_a = this, { key: ({ set value(_b) { __classPrivateFieldSet(_a, _B_privateField, _b, "f"); } }).value } = arg);
66+
}
67+
log() {
68+
console.log(__classPrivateFieldGet(this, _B_privateField, "f"));
69+
console.log(this.exposedField);
70+
}
71+
}
72+
_B_privateField = new WeakMap();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
=== tests/cases/compiler/constructorWithParameterPropertiesAndPrivateFields.es2015.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/48771
3+
4+
class A {
5+
>A : Symbol(A, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 0, 0))
6+
7+
readonly #privateField: string;
8+
>#privateField : Symbol(A.#privateField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 2, 9))
9+
10+
constructor(arg: { key: string }, public exposedField: number) {
11+
>arg : Symbol(arg, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 14))
12+
>key : Symbol(key, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 20))
13+
>exposedField : Symbol(A.exposedField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 35))
14+
15+
({ key: this.#privateField } = arg);
16+
>key : Symbol(key, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 6, 6))
17+
>this.#privateField : Symbol(A.#privateField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 2, 9))
18+
>this : Symbol(A, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 0, 0))
19+
>arg : Symbol(arg, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 14))
20+
}
21+
22+
log() {
23+
>log : Symbol(A.log, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 7, 3))
24+
25+
console.log(this.#privateField);
26+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
27+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
28+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
29+
>this.#privateField : Symbol(A.#privateField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 2, 9))
30+
>this : Symbol(A, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 0, 0))
31+
32+
console.log(this.exposedField);
33+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
34+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
35+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
36+
>this.exposedField : Symbol(A.exposedField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 35))
37+
>this : Symbol(A, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 0, 0))
38+
>exposedField : Symbol(A.exposedField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 35))
39+
}
40+
}
41+
42+
class B {
43+
>B : Symbol(B, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 13, 1))
44+
45+
readonly #privateField: string;
46+
>#privateField : Symbol(B.#privateField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 15, 9))
47+
48+
constructor(arg: { key: string }, public exposedField: number) {
49+
>arg : Symbol(arg, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 18, 14))
50+
>key : Symbol(key, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 18, 20))
51+
>exposedField : Symbol(B.exposedField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 18, 35))
52+
53+
"prologue";
54+
({ key: this.#privateField } = arg);
55+
>key : Symbol(key, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 20, 6))
56+
>this.#privateField : Symbol(B.#privateField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 15, 9))
57+
>this : Symbol(B, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 13, 1))
58+
>arg : Symbol(arg, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 18, 14))
59+
}
60+
61+
log() {
62+
>log : Symbol(B.log, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 21, 3))
63+
64+
console.log(this.#privateField);
65+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
66+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
67+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
68+
>this.#privateField : Symbol(B.#privateField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 15, 9))
69+
>this : Symbol(B, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 13, 1))
70+
71+
console.log(this.exposedField);
72+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
73+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
74+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
75+
>this.exposedField : Symbol(B.exposedField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 18, 35))
76+
>this : Symbol(B, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 13, 1))
77+
>exposedField : Symbol(B.exposedField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 18, 35))
78+
}
79+
}
80+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
=== tests/cases/compiler/constructorWithParameterPropertiesAndPrivateFields.es2015.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/48771
3+
4+
class A {
5+
>A : A
6+
7+
readonly #privateField: string;
8+
>#privateField : string
9+
10+
constructor(arg: { key: string }, public exposedField: number) {
11+
>arg : { key: string; }
12+
>key : string
13+
>exposedField : number
14+
15+
({ key: this.#privateField } = arg);
16+
>({ key: this.#privateField } = arg) : { key: string; }
17+
>{ key: this.#privateField } = arg : { key: string; }
18+
>{ key: this.#privateField } : { key: string; }
19+
>key : string
20+
>this.#privateField : string
21+
>this : this
22+
>arg : { key: string; }
23+
}
24+
25+
log() {
26+
>log : () => void
27+
28+
console.log(this.#privateField);
29+
>console.log(this.#privateField) : void
30+
>console.log : (...data: any[]) => void
31+
>console : Console
32+
>log : (...data: any[]) => void
33+
>this.#privateField : string
34+
>this : this
35+
36+
console.log(this.exposedField);
37+
>console.log(this.exposedField) : void
38+
>console.log : (...data: any[]) => void
39+
>console : Console
40+
>log : (...data: any[]) => void
41+
>this.exposedField : number
42+
>this : this
43+
>exposedField : number
44+
}
45+
}
46+
47+
class B {
48+
>B : B
49+
50+
readonly #privateField: string;
51+
>#privateField : string
52+
53+
constructor(arg: { key: string }, public exposedField: number) {
54+
>arg : { key: string; }
55+
>key : string
56+
>exposedField : number
57+
58+
"prologue";
59+
>"prologue" : "prologue"
60+
61+
({ key: this.#privateField } = arg);
62+
>({ key: this.#privateField } = arg) : { key: string; }
63+
>{ key: this.#privateField } = arg : { key: string; }
64+
>{ key: this.#privateField } : { key: string; }
65+
>key : string
66+
>this.#privateField : string
67+
>this : this
68+
>arg : { key: string; }
69+
}
70+
71+
log() {
72+
>log : () => void
73+
74+
console.log(this.#privateField);
75+
>console.log(this.#privateField) : void
76+
>console.log : (...data: any[]) => void
77+
>console : Console
78+
>log : (...data: any[]) => void
79+
>this.#privateField : string
80+
>this : this
81+
82+
console.log(this.exposedField);
83+
>console.log(this.exposedField) : void
84+
>console.log : (...data: any[]) => void
85+
>console : Console
86+
>log : (...data: any[]) => void
87+
>this.exposedField : number
88+
>this : this
89+
>exposedField : number
90+
}
91+
}
92+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// @target: es2015
2+
// https://github.com/microsoft/TypeScript/issues/48771
3+
4+
class A {
5+
readonly #privateField: string;
6+
7+
constructor(arg: { key: string }, public exposedField: number) {
8+
({ key: this.#privateField } = arg);
9+
}
10+
11+
log() {
12+
console.log(this.#privateField);
13+
console.log(this.exposedField);
14+
}
15+
}
16+
17+
class B {
18+
readonly #privateField: string;
19+
20+
constructor(arg: { key: string }, public exposedField: number) {
21+
"prologue";
22+
({ key: this.#privateField } = arg);
23+
}
24+
25+
log() {
26+
console.log(this.#privateField);
27+
console.log(this.exposedField);
28+
}
29+
}

0 commit comments

Comments
 (0)