diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 90124cef3c96f..f51a35f324a6f 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -165,6 +165,8 @@ namespace ts { return visitVariableStatement(node as VariableStatement); case SyntaxKind.ComputedPropertyName: return visitComputedPropertyName(node as ComputedPropertyName); + case SyntaxKind.CallExpression: + return visitCallExpression(node as CallExpression); default: return visitEachChild(node, visitor, context); } @@ -606,6 +608,40 @@ namespace ts { return visitEachChild(node, visitor, context); } + function visitCallExpression(node: CallExpression) { + if (isPrivateNamedPropertyAccess(node.expression)) { + // Transform call expressions of private names to properly bind the `this` parameter. + let exprForPropertyAccess: Expression = node.expression.expression; + let receiver = node.expression.expression; + if (!isSimpleInlineableExpression(node.expression.expression)) { + const generatedName = getGeneratedNameForNode(node); + hoistVariableDeclaration(generatedName); + exprForPropertyAccess = setOriginalNode( + createAssignment(generatedName, exprForPropertyAccess), + node.expression.expression + ); + receiver = generatedName; + } + return visitNode( + updateCall( + node, + createPropertyAccess( + updatePropertyAccess( + node.expression, + exprForPropertyAccess, + node.expression.name + ), + "call" + ), + /*typeArguments*/ undefined, + [receiver, ...node.arguments] + ), + visitor + ); + } + return visitEachChild(node, visitor, context); + } + function enableSubstitutionForClassAliases() { if ((enabledSubstitutions & ESNextSubstitutionFlags.ClassAliases) === 0) { enabledSubstitutions |= ESNextSubstitutionFlags.ClassAliases; diff --git a/tests/baselines/reference/privateNameFieldCallExpression.js b/tests/baselines/reference/privateNameFieldCallExpression.js new file mode 100644 index 0000000000000..b1b1260faee53 --- /dev/null +++ b/tests/baselines/reference/privateNameFieldCallExpression.js @@ -0,0 +1,29 @@ +//// [privateNameFieldCallExpression.ts] +class A { + #fieldFunc = () => this.x = 10; + x = 1; + test() { + this.#fieldFunc(); + const func = this.#fieldFunc; + func(); + } +} + + +//// [privateNameFieldCallExpression.js] +var _classPrivateFieldGet = function (receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); }; +var _fieldFunc; +var A = /** @class */ (function () { + function A() { + var _this = this; + _fieldFunc.set(this, function () { return _this.x = 10; }); + this.x = 1; + } + A.prototype.test = function () { + _classPrivateFieldGet(this, _fieldFunc).call(this); + var func = _classPrivateFieldGet(this, _fieldFunc); + func(); + }; + return A; +}()); +_fieldFunc = new WeakMap(); diff --git a/tests/baselines/reference/privateNameFieldCallExpression.symbols b/tests/baselines/reference/privateNameFieldCallExpression.symbols new file mode 100644 index 0000000000000..726a63187e70b --- /dev/null +++ b/tests/baselines/reference/privateNameFieldCallExpression.symbols @@ -0,0 +1,30 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameFieldCallExpression.ts === +class A { +>A : Symbol(A, Decl(privateNameFieldCallExpression.ts, 0, 0)) + + #fieldFunc = () => this.x = 10; +>#fieldFunc : Symbol(A[#fieldFunc], Decl(privateNameFieldCallExpression.ts, 0, 9)) +>this.x : Symbol(A.x, Decl(privateNameFieldCallExpression.ts, 1, 35)) +>this : Symbol(A, Decl(privateNameFieldCallExpression.ts, 0, 0)) +>x : Symbol(A.x, Decl(privateNameFieldCallExpression.ts, 1, 35)) + + x = 1; +>x : Symbol(A.x, Decl(privateNameFieldCallExpression.ts, 1, 35)) + + test() { +>test : Symbol(A.test, Decl(privateNameFieldCallExpression.ts, 2, 10)) + + this.#fieldFunc(); +>this.#fieldFunc : Symbol(A[#fieldFunc], Decl(privateNameFieldCallExpression.ts, 0, 9)) +>this : Symbol(A, Decl(privateNameFieldCallExpression.ts, 0, 0)) + + const func = this.#fieldFunc; +>func : Symbol(func, Decl(privateNameFieldCallExpression.ts, 5, 13)) +>this.#fieldFunc : Symbol(A[#fieldFunc], Decl(privateNameFieldCallExpression.ts, 0, 9)) +>this : Symbol(A, Decl(privateNameFieldCallExpression.ts, 0, 0)) + + func(); +>func : Symbol(func, Decl(privateNameFieldCallExpression.ts, 5, 13)) + } +} + diff --git a/tests/baselines/reference/privateNameFieldCallExpression.types b/tests/baselines/reference/privateNameFieldCallExpression.types new file mode 100644 index 0000000000000..e9d12b0d1741c --- /dev/null +++ b/tests/baselines/reference/privateNameFieldCallExpression.types @@ -0,0 +1,36 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameFieldCallExpression.ts === +class A { +>A : A + + #fieldFunc = () => this.x = 10; +>#fieldFunc : () => number +>() => this.x = 10 : () => number +>this.x = 10 : 10 +>this.x : number +>this : this +>x : number +>10 : 10 + + x = 1; +>x : number +>1 : 1 + + test() { +>test : () => void + + this.#fieldFunc(); +>this.#fieldFunc() : number +>this.#fieldFunc : () => number +>this : this + + const func = this.#fieldFunc; +>func : () => number +>this.#fieldFunc : () => number +>this : this + + func(); +>func() : number +>func : () => number + } +} + diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameFieldCallExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameFieldCallExpression.ts new file mode 100644 index 0000000000000..2d0240340bfdf --- /dev/null +++ b/tests/cases/conformance/classes/members/privateNames/privateNameFieldCallExpression.ts @@ -0,0 +1,9 @@ +class A { + #fieldFunc = () => this.x = 10; + x = 1; + test() { + this.#fieldFunc(); + const func = this.#fieldFunc; + func(); + } +}