diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 75ad50b98f3ee..ed8841c7bca26 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -149,7 +149,7 @@ namespace ts { if (capturedSuperProperties && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.SuperKeyword) { capturedSuperProperties.set(node.name.escapedText, true); } - return visitEachChild(node, visitor, context); + return visitPropertyAccessExpression(node as PropertyAccessExpression); case SyntaxKind.ElementAccessExpression: if (capturedSuperProperties && (node).expression.kind === SyntaxKind.SuperKeyword) { hasSuperElementAccess = true; @@ -583,6 +583,29 @@ namespace ts { return undefined; } + function visitPropertyAccessExpression(node: PropertyAccessExpression) { + if (isPrivateName(node.name)) { + const privateNameInfo = accessPrivateName(node.name); + if (privateNameInfo) { + switch (privateNameInfo.type) { + case PrivateNameType.InstanceField: + return setOriginalNode( + setTextRange( + createClassPrivateFieldGetHelper( + context, + visitNode(node.expression, visitor, isExpression), + privateNameInfo.weakMapName + ), + node + ), + node + ); + } + } + } + return visitEachChild(node, visitor, context); + } + function enableSubstitutionForClassAliases() { if ((enabledSubstitutions & ESNextSubstitutionFlags.ClassAliases) === 0) { enabledSubstitutions |= ESNextSubstitutionFlags.ClassAliases; @@ -1583,4 +1606,15 @@ namespace ts { location ); } + + const classPrivateFieldGetHelper: EmitHelper = { + name: "typescript:classPrivateFieldGet", + scoped: false, + text: `var _classPrivateFieldGet = function (receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); };` + }; + + function createClassPrivateFieldGetHelper(context: TransformationContext, receiver: Expression, privateField: Identifier) { + context.requestEmitHelper(classPrivateFieldGetHelper); + return createCall(getHelperName("_classPrivateFieldGet"), /* typeArguments */ undefined, [receiver, privateField]); + } } diff --git a/tests/baselines/reference/privateNameFieldAccess.js b/tests/baselines/reference/privateNameFieldAccess.js new file mode 100644 index 0000000000000..2f64fc96515fa --- /dev/null +++ b/tests/baselines/reference/privateNameFieldAccess.js @@ -0,0 +1,20 @@ +//// [privateNameFieldAccess.ts] +class A { + #myField = "hello world"; + constructor() { + console.log(this.#myField); + } +} + + +//// [privateNameFieldAccess.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 _myField; +var A = /** @class */ (function () { + function A() { + _myField.set(this, "hello world"); + console.log(_classPrivateFieldGet(this, _myField)); + } + return A; +}()); +_myField = new WeakMap(); diff --git a/tests/baselines/reference/privateNameFieldAccess.symbols b/tests/baselines/reference/privateNameFieldAccess.symbols new file mode 100644 index 0000000000000..ea011b99d9a27 --- /dev/null +++ b/tests/baselines/reference/privateNameFieldAccess.symbols @@ -0,0 +1,17 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameFieldAccess.ts === +class A { +>A : Symbol(A, Decl(privateNameFieldAccess.ts, 0, 0)) + + #myField = "hello world"; +>#myField : Symbol(A[#myField], Decl(privateNameFieldAccess.ts, 0, 9)) + + constructor() { + console.log(this.#myField); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>this.#myField : Symbol(A[#myField], Decl(privateNameFieldAccess.ts, 0, 9)) +>this : Symbol(A, Decl(privateNameFieldAccess.ts, 0, 0)) + } +} + diff --git a/tests/baselines/reference/privateNameFieldAccess.types b/tests/baselines/reference/privateNameFieldAccess.types new file mode 100644 index 0000000000000..17568587e311d --- /dev/null +++ b/tests/baselines/reference/privateNameFieldAccess.types @@ -0,0 +1,19 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameFieldAccess.ts === +class A { +>A : A + + #myField = "hello world"; +>#myField : string +>"hello world" : "hello world" + + constructor() { + console.log(this.#myField); +>console.log(this.#myField) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>this.#myField : string +>this : this + } +} + diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameFieldAccess.ts b/tests/cases/conformance/classes/members/privateNames/privateNameFieldAccess.ts new file mode 100644 index 0000000000000..2237195ddc9ec --- /dev/null +++ b/tests/cases/conformance/classes/members/privateNames/privateNameFieldAccess.ts @@ -0,0 +1,6 @@ +class A { + #myField = "hello world"; + constructor() { + console.log(this.#myField); + } +}