Skip to content

Commit 74c337b

Browse files
committed
Fix handling of prologue statements when there are
parameter property declarations If there is a prologue (and no super) in a constructor, the prologue won't be skipped when calculating the parameter properties, leading to the prologue being included twice
1 parent 2e619fd commit 74c337b

5 files changed

+155
-6
lines changed

src/compiler/transformers/classFields.ts

Lines changed: 9 additions & 6 deletions
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,7 +1357,7 @@ 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) {
@@ -1373,7 +1376,7 @@ namespace ts {
13731376
statements = [...parameterProperties, ...statements];
13741377
}
13751378

1376-
indexOfFirstStatementAfterSuper += parameterPropertyDeclarationCount;
1379+
indexOfFirstStatementAfterSuperAndPrologue += parameterPropertyDeclarationCount;
13771380
}
13781381
}
13791382
}
@@ -1385,7 +1388,7 @@ namespace ts {
13851388

13861389
// Add existing statements after the initial prologues and super call
13871390
if (constructor) {
1388-
addRange(statements, visitNodes(constructor.body!.statements, visitBodyStatement, isStatement, indexOfFirstStatementAfterSuper + prologueStatementCount));
1391+
addRange(statements, visitNodes(constructor.body!.statements, visitBodyStatement, isStatement, indexOfFirstStatementAfterSuperAndPrologue));
13891392
}
13901393

13911394
statements = factory.mergeLexicalEnvironment(statements, endLexicalEnvironment());
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
18+
//// [constructorWithParameterPropertiesAndPrivateFields.es2015.js]
19+
// https://github.com/microsoft/TypeScript/issues/48771
20+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
21+
if (kind === "m") throw new TypeError("Private method is not writable");
22+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
23+
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");
24+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
25+
};
26+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
27+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
28+
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");
29+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
30+
};
31+
var _A_privateField;
32+
class A {
33+
constructor(arg, exposedField) {
34+
this.exposedField = exposedField;
35+
var _a;
36+
_A_privateField.set(this, void 0);
37+
(_a = this, { key: ({ set value(_b) { __classPrivateFieldSet(_a, _A_privateField, _b, "f"); } }).value } = arg);
38+
}
39+
log() {
40+
console.log(__classPrivateFieldGet(this, _A_privateField, "f"));
41+
console.log(this.exposedField);
42+
}
43+
}
44+
_A_privateField = new WeakMap();
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
}

0 commit comments

Comments
 (0)