Repository: swc-project/swc (rust)

Issue:
TypeScript - Support `typeof` on private fields
### Describe the feature

https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-beta/#typeof-on-private-fields

```ts
class Container {
    #data = "hello!";

    get data(): typeof this.#data {
        return this.#data;
    }

    set data(value: typeof this.#data) {
        this.#data = value;
    }
}
```

### Babel plugin or link to the feature description

_No response_

### Additional context

Currently errors:

https://play.swc.rs/?version=1.2.157&code=H4sIAAAAAAAAA0vOSSwuVnDOzytJzMxLLVKo5uVSAALllMSSRAVbBaWM1JycfEUla14uiER6aokCSE5D00qhpLIgNT9NoSQjs1gPogGqGwSKUktKi%2FKQJK0hcrUwk4phJpUl5pSmYjFNE9k4JEtsFcA6EObVAgCfa7ZexQAAAA%3D%3D&config=H4sIAAAAAAAAA0WOQQ6DMAwE%2F%2BIz1%2FaQP%2FCIKBiUipDIa6RGiL%2FXoaG9edfeWR%2F0QiB3UPECljahburf5EhrYQSJRWkghVmzX8GnCS8Lq50wHrZbcwaTU9l5oBS3ONcGCjkVYaDnbOW3ZeUbY5yUp70Zx9V18Z7Gg0oM%2Bot95Zgn%2Fn9wt%2FSTiLGj2hPnBwXfhU7WAAAA

Pull request:
feat(es/parser): typeof on #private Fields
<!-- Note: CI script will automatically rebase your PR so please do not rebase unless required -->

<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

**BREAKING CHANGE:**
- swc_ecma_ast
- swc_ecma_codegen
- swc_ecma_parser
- swc_ecma_transforms_proposal
- swc_ecma_transforms_typescript
- swc_ecma_visit
- swc_estree_ast
- swc_estree_compat

<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**
- close #4287
- https://github.com/microsoft/TypeScript/pull/47696

Patch:
diff --git a/crates/swc_ecma_ast/src/lib.rs b/crates/swc_ecma_ast/src/lib.rs
index ba952a2844..c3c9cfb8eb 100644
--- a/crates/swc_ecma_ast/src/lib.rs
+++ b/crates/swc_ecma_ast/src/lib.rs
@@ -62,14 +62,15 @@ pub use self::{
         TsExprWithTypeArgs, TsExternalModuleRef, TsFnOrConstructorType, TsFnParam, TsFnType,
         TsGetterSignature, TsImportEqualsDecl, TsImportType, TsIndexSignature, TsIndexedAccessType,
         TsInferType, TsInstantiation, TsInterfaceBody, TsInterfaceDecl, TsIntersectionType,
-        TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType, TsMappedType, TsMethodSignature,
-        TsModuleBlock, TsModuleDecl, TsModuleName, TsModuleRef, TsNamespaceBody, TsNamespaceDecl,
-        TsNamespaceExportDecl, TsNonNullExpr, TsOptionalType, TsParamProp, TsParamPropParam,
-        TsParenthesizedType, TsPropertySignature, TsQualifiedName, TsRestType, TsSetterSignature,
-        TsThisType, TsThisTypeOrIdent, TsTplLitType, TsTupleElement, TsTupleType, TsType,
-        TsTypeAliasDecl, TsTypeAnn, TsTypeAssertion, TsTypeElement, TsTypeLit, TsTypeOperator,
-        TsTypeOperatorOp, TsTypeParam, TsTypeParamDecl, TsTypeParamInstantiation, TsTypePredicate,
-        TsTypeQuery, TsTypeQueryExpr, TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
+        TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType, TsMappedType, TsMemberName,
+        TsMethodSignature, TsModuleBlock, TsModuleDecl, TsModuleName, TsModuleRef, TsNamespaceBody,
+        TsNamespaceDecl, TsNamespaceExportDecl, TsNonNullExpr, TsOptionalType, TsParamProp,
+        TsParamPropParam, TsParenthesizedType, TsPropertySignature, TsQualifiedName, TsRestType,
+        TsSetterSignature, TsThisType, TsThisTypeOrIdent, TsTplLitType, TsTupleElement,
+        TsTupleType, TsType, TsTypeAliasDecl, TsTypeAnn, TsTypeAssertion, TsTypeElement, TsTypeLit,
+        TsTypeOperator, TsTypeOperatorOp, TsTypeParam, TsTypeParamDecl, TsTypeParamInstantiation,
+        TsTypePredicate, TsTypeQuery, TsTypeQueryExpr, TsTypeRef, TsUnionOrIntersectionType,
+        TsUnionType,
     },
 };
 
diff --git a/crates/swc_ecma_ast/src/typescript.rs b/crates/swc_ecma_ast/src/typescript.rs
index a5693f95b6..a9b41346b1 100644
--- a/crates/swc_ecma_ast/src/typescript.rs
+++ b/crates/swc_ecma_ast/src/typescript.rs
@@ -18,7 +18,7 @@ use crate::{
     lit::{Bool, Number, Str},
     module::ModuleItem,
     pat::{ArrayPat, AssignPat, ObjectPat, Pat, RestPat},
-    BigInt, BindingIdent, TplElement,
+    BigInt, BindingIdent, PrivateName, TplElement,
 };
 
 #[ast_node("TsTypeAnnotation")]
@@ -101,7 +101,7 @@ pub struct TsQualifiedName {
     #[span(lo)]
     pub left: TsEntityName,
     #[span(hi)]
-    pub right: Ident,
+    pub right: TsMemberName,
 }
 
 #[ast_node]
@@ -116,6 +116,18 @@ pub enum TsEntityName {
     Ident(Ident),
 }
 
+#[ast_node]
+#[derive(Eq, Hash, Is, EqIgnoreSpan)]
+#[allow(variant_size_differences)]
+#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+pub enum TsMemberName {
+    #[tag("Identifier")]
+    Ident(Ident),
+
+    #[tag("PrivateName")]
+    PrivateName(PrivateName),
+}
+
 // ================
 // TypeScript type members (for type literal / interface / class)
 // ================
diff --git a/crates/swc_ecma_codegen/src/typescript.rs b/crates/swc_ecma_codegen/src/typescript.rs
index 51cc20af39..36443553a9 100644
--- a/crates/swc_ecma_codegen/src/typescript.rs
+++ b/crates/swc_ecma_codegen/src/typescript.rs
@@ -137,6 +137,16 @@ where
         }
     }
 
+    #[emitter]
+    fn emit_ts_member_name(&mut self, n: &TsMemberName) -> Result {
+        self.emit_leading_comments_of_span(n.span(), false)?;
+
+        match n {
+            TsMemberName::Ident(n) => emit!(n),
+            TsMemberName::PrivateName(n) => emit!(n),
+        }
+    }
+
     #[emitter]
     fn emit_ts_enum_decl(&mut self, n: &TsEnumDecl) -> Result {
         self.emit_leading_comments_of_span(n.span(), false)?;
diff --git a/crates/swc_ecma_parser/src/parser/typescript.rs b/crates/swc_ecma_parser/src/parser/typescript.rs
index 05e2d72228..3738716fd9 100644
--- a/crates/swc_ecma_parser/src/parser/typescript.rs
+++ b/crates/swc_ecma_parser/src/parser/typescript.rs
@@ -180,7 +180,11 @@ impl<I: Tokens> Parser<I> {
     }
 
     /// `tsParseEntityName`
-    fn parse_ts_entity_name(&mut self, allow_reserved_words: bool) -> PResult<TsEntityName> {
+    fn parse_ts_entity_name(
+        &mut self,
+        allow_reserved_words: bool,
+        allow_private_identifiers: bool,
+    ) -> PResult<TsEntityName> {
         debug_assert!(self.input.syntax().typescript());
 
         let init = self.parse_ident_name()?;
@@ -196,7 +200,7 @@ impl<I: Tokens> Parser<I> {
         let mut entity = TsEntityName::Ident(init);
         while eat!(self, '.') {
             let dot_start = cur_pos!(self);
-            if !is!(self, '#') && !is!(self, IdentName) {
+            if !allow_private_identifiers && !is!(self, '#') && !is!(self, IdentName) {
                 self.emit_err(
                     Span::new(dot_start, dot_start, Default::default()),
                     SyntaxError::TS1003,
@@ -205,10 +209,13 @@ impl<I: Tokens> Parser<I> {
             }
 
             let left = entity;
-            let right = if allow_reserved_words {
-                self.parse_ident_name()?
+            let right = if allow_private_identifiers {
+                self.parse_maybe_private_name()?
+                    .either(Into::into, Into::into)
+            } else if allow_reserved_words {
+                self.parse_ident_name()?.into()
             } else {
-                self.parse_ident(false, false)?
+                self.parse_ident(false, false)?.into()
             };
             entity = TsEntityName::TsQualifiedName(Box::new(TsQualifiedName { left, right }));
         }
@@ -225,7 +232,9 @@ impl<I: Tokens> Parser<I> {
 
         let has_modifier = self.eat_any_ts_modifier()?;
 
-        let type_name = self.parse_ts_entity_name(/* allow_reserved_words */ true)?;
+        let type_name = self.parse_ts_entity_name(
+            /* allow_reserved_words */ true, /* allow_private_identifiers */ false,
+        )?;
         trace_cur!(self, parse_ts_type_ref__type_args);
         let type_params = if !self.input.had_line_break_before_cur() && is!(self, '<') {
             Some(self.parse_ts_type_args()?)
@@ -317,7 +326,7 @@ impl<I: Tokens> Parser<I> {
         expect!(self, ')');
 
         let qualifier = if eat!(self, '.') {
-            self.parse_ts_entity_name(false).map(Some)?
+            self.parse_ts_entity_name(false, false).map(Some)?
         } else {
             None
         };
@@ -347,7 +356,7 @@ impl<I: Tokens> Parser<I> {
         } else {
             self.parse_ts_entity_name(
                 // allow_reserved_word
-                true,
+                true, true,
             )
             .map(From::from)?
         };
@@ -1126,8 +1135,10 @@ impl<I: Tokens> Parser<I> {
         if self.is_ts_external_module_ref()? {
             self.parse_ts_external_module_ref().map(From::from)
         } else {
-            self.parse_ts_entity_name(/* allow_reserved_words */ false)
-                .map(From::from)
+            self.parse_ts_entity_name(
+                /* allow_reserved_words */ false, /* allow_private_identifiers */ false,
+            )
+            .map(From::from)
         }
     }
 
diff --git a/crates/swc_ecma_transforms_proposal/src/decorators/legacy/metadata.rs b/crates/swc_ecma_transforms_proposal/src/decorators/legacy/metadata.rs
index 4d39d45e6b..741dccaf97 100644
--- a/crates/swc_ecma_transforms_proposal/src/decorators/legacy/metadata.rs
+++ b/crates/swc_ecma_transforms_proposal/src/decorators/legacy/metadata.rs
@@ -574,7 +574,10 @@ fn ts_entity_to_member_expr(type_name: &TsEntityName) -> Expr {
             Expr::Member(MemberExpr {
                 span: DUMMY_SP,
                 obj: obj.into(),
-                prop: MemberProp::Ident(q.right.clone()),
+                prop: match q.right.clone() {
+                    TsMemberName::Ident(i) => MemberProp::Ident(i),
+                    TsMemberName::PrivateName(p) => MemberProp::PrivateName(p),
+                },
             })
         }
         TsEntityName::Ident(i) => Expr::Ident(i.clone()),
diff --git a/crates/swc_ecma_transforms_typescript/src/strip.rs b/crates/swc_ecma_transforms_typescript/src/strip.rs
index 21c5372184..ab98db7b74 100644
--- a/crates/swc_ecma_transforms_typescript/src/strip.rs
+++ b/crates/swc_ecma_transforms_typescript/src/strip.rs
@@ -2549,7 +2549,10 @@ fn ts_entity_name_to_expr(n: TsEntityName) -> Expr {
             MemberExpr {
                 span: DUMMY_SP,
                 obj: Box::new(ts_entity_name_to_expr(left)),
-                prop: MemberProp::Ident(right),
+                prop: match right {
+                    TsMemberName::Ident(i) => MemberProp::Ident(i),
+                    TsMemberName::PrivateName(p) => MemberProp::PrivateName(p),
+                },
             }
             .into()
         }
diff --git a/crates/swc_ecma_visit/src/lib.rs b/crates/swc_ecma_visit/src/lib.rs
index dd675f5977..5bed611a92 100644
--- a/crates/swc_ecma_visit/src/lib.rs
+++ b/crates/swc_ecma_visit/src/lib.rs
@@ -309,6 +309,7 @@ macro_rules! noop_fold_type {
         noop_fold_type!(fold_ts_construct_signature_decl, TsConstructSignatureDecl);
         noop_fold_type!(fold_ts_constructor_type, TsConstructorType);
         noop_fold_type!(fold_ts_entity_name, TsEntityName);
+        noop_fold_type!(fold_ts_member_name, TsMemberName);
         noop_fold_type!(fold_ts_enum_decl, TsEnumDecl);
         noop_fold_type!(fold_ts_enum_member, TsEnumMember);
         noop_fold_type!(fold_ts_enum_member_id, TsEnumMemberId);
@@ -384,6 +385,7 @@ macro_rules! noop_visit_type {
         noop_visit_type!(visit_ts_construct_signature_decl, TsConstructSignatureDecl);
         noop_visit_type!(visit_ts_constructor_type, TsConstructorType);
         noop_visit_type!(visit_ts_entity_name, TsEntityName);
+        noop_visit_type!(visit_ts_member_name, TsMemberName);
         noop_visit_type!(visit_ts_external_module_ref, TsExternalModuleRef);
         noop_visit_type!(visit_ts_fn_or_constructor_type, TsFnOrConstructorType);
         noop_visit_type!(visit_ts_fn_param, TsFnParam);
@@ -449,6 +451,7 @@ macro_rules! noop_visit_mut_type {
         );
         noop_visit_mut_type!(visit_mut_ts_constructor_type, TsConstructorType);
         noop_visit_mut_type!(visit_mut_ts_entity_name, TsEntityName);
+        noop_visit_mut_type!(visit_mut_ts_member_name, TsMemberName);
         noop_visit_mut_type!(visit_mut_ts_external_module_ref, TsExternalModuleRef);
         noop_visit_mut_type!(visit_mut_ts_fn_or_constructor_type, TsFnOrConstructorType);
         noop_visit_mut_type!(visit_mut_ts_fn_param, TsFnParam);
@@ -1425,12 +1428,16 @@ define!({
     }
     pub struct TsQualifiedName {
         pub left: TsEntityName,
-        pub right: Ident,
+        pub right: TsMemberName,
     }
     pub enum TsEntityName {
         TsQualifiedName(Box<TsQualifiedName>),
         Ident(Ident),
     }
+    pub enum TsMemberName {
+        Ident(Ident),
+        PrivateName(PrivateName),
+    }
     pub enum TsTypeElement {
         TsCallSignatureDecl(TsCallSignatureDecl),
         TsConstructSignatureDecl(TsConstructSignatureDecl),
diff --git a/crates/swc_estree_ast/src/typescript.rs b/crates/swc_estree_ast/src/typescript.rs
index 128238cc96..8f4030b0c8 100644
--- a/crates/swc_estree_ast/src/typescript.rs
+++ b/crates/swc_estree_ast/src/typescript.rs
@@ -10,6 +10,7 @@ use crate::{
     object::ObjectKey,
     pat::AssignmentPattern,
     stmt::Statement,
+    PrivateName,
 };
 
 #[derive(Debug, Clone, PartialEq)]
@@ -248,7 +249,8 @@ pub struct TSQualifiedName {
     #[serde(flatten)]
     pub base: BaseNode,
     pub left: Box<TSEntityName>,
-    pub right: Identifier,
+    #[serde(flatten)]
+    pub right: TSMemberName,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@@ -467,6 +469,15 @@ pub enum TSEntityName {
     Qualified(TSQualifiedName),
 }
 
+#[derive(Debug, Clone, PartialEq)]
+#[ast_serde]
+pub enum TSMemberName {
+    #[tag("Identifier")]
+    Id(Identifier),
+    #[tag("PrivateName")]
+    PrivateName(PrivateName),
+}
+
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 #[serde(rename_all = "camelCase")]
 #[serde(tag = "type")]
diff --git a/crates/swc_estree_compat/src/babelify/typescript.rs b/crates/swc_estree_compat/src/babelify/typescript.rs
index d70c037d32..0d628a244a 100644
--- a/crates/swc_estree_compat/src/babelify/typescript.rs
+++ b/crates/swc_estree_compat/src/babelify/typescript.rs
@@ -9,14 +9,14 @@ use swc_ecma_ast::{
     TsExprWithTypeArgs, TsExternalModuleRef, TsFnOrConstructorType, TsFnParam, TsFnType,
     TsImportEqualsDecl, TsImportType, TsIndexSignature, TsIndexedAccessType, TsInferType,
     TsInterfaceBody, TsInterfaceDecl, TsIntersectionType, TsKeywordType, TsKeywordTypeKind, TsLit,
-    TsLitType, TsMappedType, TsMethodSignature, TsModuleBlock, TsModuleDecl, TsModuleName,
-    TsModuleRef, TsNamespaceBody, TsNamespaceDecl, TsNamespaceExportDecl, TsNonNullExpr,
-    TsOptionalType, TsParamProp, TsParamPropParam, TsParenthesizedType, TsPropertySignature,
-    TsQualifiedName, TsRestType, TsThisType, TsThisTypeOrIdent, TsTplLitType, TsTupleElement,
-    TsTupleType, TsType, TsTypeAliasDecl, TsTypeAnn, TsTypeAssertion, TsTypeElement, TsTypeLit,
-    TsTypeOperator, TsTypeOperatorOp, TsTypeParam, TsTypeParamDecl, TsTypeParamInstantiation,
-    TsTypePredicate, TsTypeQuery, TsTypeQueryExpr, TsTypeRef, TsUnionOrIntersectionType,
-    TsUnionType,
+    TsLitType, TsMappedType, TsMemberName, TsMethodSignature, TsModuleBlock, TsModuleDecl,
+    TsModuleName, TsModuleRef, TsNamespaceBody, TsNamespaceDecl, TsNamespaceExportDecl,
+    TsNonNullExpr, TsOptionalType, TsParamProp, TsParamPropParam, TsParenthesizedType,
+    TsPropertySignature, TsQualifiedName, TsRestType, TsThisType, TsThisTypeOrIdent, TsTplLitType,
+    TsTupleElement, TsTupleType, TsType, TsTypeAliasDecl, TsTypeAnn, TsTypeAssertion,
+    TsTypeElement, TsTypeLit, TsTypeOperator, TsTypeOperatorOp, TsTypeParam, TsTypeParamDecl,
+    TsTypeParamInstantiation, TsTypePredicate, TsTypeQuery, TsTypeQueryExpr, TsTypeRef,
+    TsUnionOrIntersectionType, TsUnionType,
 };
 use swc_estree_ast::{
     Access, ArrayPattern, IdOrRest, IdOrString, Identifier, ObjectPattern, RestElement,
@@ -26,8 +26,8 @@ use swc_estree_ast::{
     TSExpressionWithTypeArguments, TSExternalModuleReference, TSFunctionType,
     TSImportEqualsDeclModuleRef, TSImportEqualsDeclaration, TSImportType, TSIndexSignature,
     TSIndexedAccessType, TSInferType, TSInterfaceBody, TSInterfaceDeclaration, TSIntersectionType,
-    TSIntrinsicKeyword, TSLiteralType, TSLiteralTypeLiteral, TSMappedType, TSMethodSignature,
-    TSModuleBlock, TSModuleDeclBody, TSModuleDeclaration, TSNamedTupleMember,
+    TSIntrinsicKeyword, TSLiteralType, TSLiteralTypeLiteral, TSMappedType, TSMemberName,
+    TSMethodSignature, TSModuleBlock, TSModuleDeclBody, TSModuleDeclaration, TSNamedTupleMember,
     TSNamespaceExportDeclaration, TSNeverKeyword, TSNonNullExpression, TSNullKeyword,
     TSNumberKeyword, TSObjectKeyword, TSOptionalType, TSParamPropParam, TSParameterProperty,
     TSParenthesizedType, TSPropertySignature, TSQualifiedName, TSRestType, TSStringKeyword,
@@ -192,6 +192,17 @@ impl Babelify for TsEntityName {
     }
 }
 
+impl Babelify for TsMemberName {
+    type Output = TSMemberName;
+
+    fn babelify(self, ctx: &Context) -> Self::Output {
+        match self {
+            Self::Ident(i) => Self::Output::Id(i.babelify(ctx)),
+            Self::PrivateName(p) => Self::Output::PrivateName(p.babelify(ctx)),
+        }
+    }
+}
+
 impl Babelify for TsTypeElement {
     type Output = TSTypeElement;
 
@@ -842,7 +853,8 @@ impl Babelify for TsExprWithTypeArgs {
                     base: ctx.base(e.span),
                     left: Box::new(babelify_expr(*e.obj, ctx)),
                     right: match e.prop {
-                        MemberProp::Ident(id) => id.babelify(ctx),
+                        MemberProp::Ident(id) => TSMemberName::Id(id.babelify(ctx)),
+                        MemberProp::PrivateName(p) => TSMemberName::PrivateName(p.babelify(ctx)),
                         _ => unreachable!(),
                     },
                 }),
diff --git a/crates/swc_estree_compat/src/swcify/class.rs b/crates/swc_estree_compat/src/swcify/class.rs
index eda0f94b53..63bd72e644 100644
--- a/crates/swc_estree_compat/src/swcify/class.rs
+++ b/crates/swc_estree_compat/src/swcify/class.rs
@@ -1,6 +1,6 @@
 use swc_ecma_ast::{
     ClassMember, Expr, Function, MemberExpr, MemberProp, MethodKind, ParamOrTsParamProp,
-    TsExprWithTypeArgs,
+    TsExprWithTypeArgs, TsMemberName,
 };
 use swc_estree_ast::{
     ClassBody, ClassBodyEl, ClassImpl, ClassMethodKind, TSEntityName,
@@ -198,7 +198,10 @@ impl Swcify for TSExpressionWithTypeArguments {
         fn swcify_qualified_name(qualified_name: TSQualifiedName, ctx: &Context) -> Box<Expr> {
             Box::new(Expr::Member(MemberExpr {
                 obj: swcify_expr(*qualified_name.left, ctx),
-                prop: MemberProp::Ident(qualified_name.right.swcify(ctx).id),
+                prop: match qualified_name.right.swcify(ctx) {
+                    TsMemberName::Ident(id) => MemberProp::Ident(id),
+                    TsMemberName::PrivateName(p) => MemberProp::PrivateName(p),
+                },
                 span: ctx.span(&qualified_name.base),
             }))
         }
diff --git a/crates/swc_estree_compat/src/swcify/typescript.rs b/crates/swc_estree_compat/src/swcify/typescript.rs
index 9be83ae058..2da16fe346 100644
--- a/crates/swc_estree_compat/src/swcify/typescript.rs
+++ b/crates/swc_estree_compat/src/swcify/typescript.rs
@@ -1,11 +1,11 @@
 use swc_ecma_ast::{
-    Accessibility, Ident, TsEntityName, TsQualifiedName, TsType, TsTypeAnn, TsTypeParam,
-    TsTypeParamDecl, TsTypeParamInstantiation,
+    Accessibility, Ident, TsEntityName, TsMemberName, TsQualifiedName, TsType, TsTypeAnn,
+    TsTypeParam, TsTypeParamDecl, TsTypeParamInstantiation,
 };
 use swc_estree_ast::{
-    Access, FlowType, SuperTypeParams, TSEntityName, TSQualifiedName, TSType, TSTypeAnnotation,
-    TSTypeParameter, TSTypeParameterDeclaration, TSTypeParameterInstantiation, TypeAnnotOrNoop,
-    TypeParamDeclOrNoop,
+    Access, FlowType, SuperTypeParams, TSEntityName, TSMemberName, TSQualifiedName, TSType,
+    TSTypeAnnotation, TSTypeParameter, TSTypeParameterDeclaration, TSTypeParameterInstantiation,
+    TypeAnnotOrNoop, TypeParamDeclOrNoop,
 };
 
 use super::Context;
@@ -140,7 +140,18 @@ impl Swcify for TSQualifiedName {
     fn swcify(self, ctx: &Context) -> Self::Output {
         TsQualifiedName {
             left: self.left.swcify(ctx),
-            right: self.right.swcify(ctx).id,
+            right: self.right.swcify(ctx),
+        }
+    }
+}
+
+impl Swcify for TSMemberName {
+    type Output = TsMemberName;
+
+    fn swcify(self, ctx: &Context) -> Self::Output {
+        match self {
+            Self::Id(id) => Self::Output::Ident(id.swcify(ctx).id),
+            Self::PrivateName(p) => Self::Output::Ident(p.swcify(ctx).id),
         }
     }
 }


Test patch:
diff --git a/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/1/input.ts b/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/1/input.ts
new file mode 100644
index 0000000000..95d8b82a0a
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/1/input.ts
@@ -0,0 +1,8 @@
+class C {
+    #a = 'a';
+
+    constructor() {
+        const a: typeof this.#a = ''; // Ok
+        const b: typeof this.#a = 1;  // Error
+    }
+}
\ No newline at end of file
diff --git a/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/1/input.ts.json b/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/1/input.ts.json
new file mode 100644
index 0000000000..8ea98bf289
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/1/input.ts.json
@@ -0,0 +1,291 @@
+{
+  "type": "Script",
+  "span": {
+    "start": 0,
+    "end": 143,
+    "ctxt": 0
+  },
+  "body": [
+    {
+      "type": "ClassDeclaration",
+      "identifier": {
+        "type": "Identifier",
+        "span": {
+          "start": 6,
+          "end": 7,
+          "ctxt": 0
+        },
+        "value": "C",
+        "optional": false
+      },
+      "declare": false,
+      "span": {
+        "start": 0,
+        "end": 143,
+        "ctxt": 0
+      },
+      "decorators": [],
+      "body": [
+        {
+          "type": "PrivateProperty",
+          "span": {
+            "start": 14,
+            "end": 23,
+            "ctxt": 0
+          },
+          "key": {
+            "type": "PrivateName",
+            "span": {
+              "start": 14,
+              "end": 16,
+              "ctxt": 0
+            },
+            "id": {
+              "type": "Identifier",
+              "span": {
+                "start": 15,
+                "end": 16,
+                "ctxt": 0
+              },
+              "value": "a",
+              "optional": false
+            }
+          },
+          "value": {
+            "type": "StringLiteral",
+            "span": {
+              "start": 19,
+              "end": 22,
+              "ctxt": 0
+            },
+            "value": "a",
+            "raw": "'a'"
+          },
+          "typeAnnotation": null,
+          "isStatic": false,
+          "decorators": [],
+          "accessibility": null,
+          "isOptional": false,
+          "isOverride": false,
+          "readonly": false,
+          "definite": false
+        },
+        {
+          "type": "Constructor",
+          "span": {
+            "start": 29,
+            "end": 141,
+            "ctxt": 0
+          },
+          "key": {
+            "type": "Identifier",
+            "span": {
+              "start": 29,
+              "end": 40,
+              "ctxt": 0
+            },
+            "value": "constructor",
+            "optional": false
+          },
+          "params": [],
+          "body": {
+            "type": "BlockStatement",
+            "span": {
+              "start": 43,
+              "end": 141,
+              "ctxt": 0
+            },
+            "stmts": [
+              {
+                "type": "VariableDeclaration",
+                "span": {
+                  "start": 53,
+                  "end": 82,
+                  "ctxt": 0
+                },
+                "kind": "const",
+                "declare": false,
+                "declarations": [
+                  {
+                    "type": "VariableDeclarator",
+                    "span": {
+                      "start": 59,
+                      "end": 81,
+                      "ctxt": 0
+                    },
+                    "id": {
+                      "type": "Identifier",
+                      "span": {
+                        "start": 59,
+                        "end": 60,
+                        "ctxt": 0
+                      },
+                      "value": "a",
+                      "optional": false,
+                      "typeAnnotation": {
+                        "type": "TsTypeAnnotation",
+                        "span": {
+                          "start": 60,
+                          "end": 76,
+                          "ctxt": 0
+                        },
+                        "typeAnnotation": {
+                          "type": "TsTypeQuery",
+                          "span": {
+                            "start": 62,
+                            "end": 76,
+                            "ctxt": 0
+                          },
+                          "exprName": {
+                            "type": "TsQualifiedName",
+                            "left": {
+                              "type": "Identifier",
+                              "span": {
+                                "start": 69,
+                                "end": 73,
+                                "ctxt": 0
+                              },
+                              "value": "this",
+                              "optional": false
+                            },
+                            "right": {
+                              "type": "PrivateName",
+                              "span": {
+                                "start": 74,
+                                "end": 76,
+                                "ctxt": 0
+                              },
+                              "id": {
+                                "type": "Identifier",
+                                "span": {
+                                  "start": 75,
+                                  "end": 76,
+                                  "ctxt": 0
+                                },
+                                "value": "a",
+                                "optional": false
+                              }
+                            }
+                          },
+                          "typeArguments": null
+                        }
+                      }
+                    },
+                    "init": {
+                      "type": "StringLiteral",
+                      "span": {
+                        "start": 79,
+                        "end": 81,
+                        "ctxt": 0
+                      },
+                      "value": "",
+                      "raw": "''"
+                    },
+                    "definite": false
+                  }
+                ]
+              },
+              {
+                "type": "VariableDeclaration",
+                "span": {
+                  "start": 97,
+                  "end": 125,
+                  "ctxt": 0
+                },
+                "kind": "const",
+                "declare": false,
+                "declarations": [
+                  {
+                    "type": "VariableDeclarator",
+                    "span": {
+                      "start": 103,
+                      "end": 124,
+                      "ctxt": 0
+                    },
+                    "id": {
+                      "type": "Identifier",
+                      "span": {
+                        "start": 103,
+                        "end": 104,
+                        "ctxt": 0
+                      },
+                      "value": "b",
+                      "optional": false,
+                      "typeAnnotation": {
+                        "type": "TsTypeAnnotation",
+                        "span": {
+                          "start": 104,
+                          "end": 120,
+                          "ctxt": 0
+                        },
+                        "typeAnnotation": {
+                          "type": "TsTypeQuery",
+                          "span": {
+                            "start": 106,
+                            "end": 120,
+                            "ctxt": 0
+                          },
+                          "exprName": {
+                            "type": "TsQualifiedName",
+                            "left": {
+                              "type": "Identifier",
+                              "span": {
+                                "start": 113,
+                                "end": 117,
+                                "ctxt": 0
+                              },
+                              "value": "this",
+                              "optional": false
+                            },
+                            "right": {
+                              "type": "PrivateName",
+                              "span": {
+                                "start": 118,
+                                "end": 120,
+                                "ctxt": 0
+                              },
+                              "id": {
+                                "type": "Identifier",
+                                "span": {
+                                  "start": 119,
+                                  "end": 120,
+                                  "ctxt": 0
+                                },
+                                "value": "a",
+                                "optional": false
+                              }
+                            }
+                          },
+                          "typeArguments": null
+                        }
+                      }
+                    },
+                    "init": {
+                      "type": "NumericLiteral",
+                      "span": {
+                        "start": 123,
+                        "end": 124,
+                        "ctxt": 0
+                      },
+                      "value": 1.0,
+                      "raw": "1"
+                    },
+                    "definite": false
+                  }
+                ]
+              }
+            ]
+          },
+          "accessibility": null,
+          "isOptional": false
+        }
+      ],
+      "superClass": null,
+      "isAbstract": false,
+      "typeParams": null,
+      "superTypeParams": null,
+      "implements": []
+    }
+  ],
+  "interpreter": null
+}
diff --git a/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/2/input.ts b/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/2/input.ts
new file mode 100644
index 0000000000..028caeaf4b
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/2/input.ts
@@ -0,0 +1,11 @@
+class Container {
+    #data = "hello!";
+
+    get data(): typeof this.#data {
+        return this.#data;
+    }
+
+    set data(value: typeof this.#data) {
+        this.#data = value;
+    }
+}
\ No newline at end of file
diff --git a/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/2/input.ts.json b/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/2/input.ts.json
new file mode 100644
index 0000000000..3b496282d0
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/private-name-in-type-query/2/input.ts.json
@@ -0,0 +1,394 @@
+{
+  "type": "Script",
+  "span": {
+    "start": 0,
+    "end": 187,
+    "ctxt": 0
+  },
+  "body": [
+    {
+      "type": "ClassDeclaration",
+      "identifier": {
+        "type": "Identifier",
+        "span": {
+          "start": 6,
+          "end": 15,
+          "ctxt": 0
+        },
+        "value": "Container",
+        "optional": false
+      },
+      "declare": false,
+      "span": {
+        "start": 0,
+        "end": 187,
+        "ctxt": 0
+      },
+      "decorators": [],
+      "body": [
+        {
+          "type": "PrivateProperty",
+          "span": {
+            "start": 22,
+            "end": 39,
+            "ctxt": 0
+          },
+          "key": {
+            "type": "PrivateName",
+            "span": {
+              "start": 22,
+              "end": 27,
+              "ctxt": 0
+            },
+            "id": {
+              "type": "Identifier",
+              "span": {
+                "start": 23,
+                "end": 27,
+                "ctxt": 0
+              },
+              "value": "data",
+              "optional": false
+            }
+          },
+          "value": {
+            "type": "StringLiteral",
+            "span": {
+              "start": 30,
+              "end": 38,
+              "ctxt": 0
+            },
+            "value": "hello!",
+            "raw": "\"hello!\""
+          },
+          "typeAnnotation": null,
+          "isStatic": false,
+          "decorators": [],
+          "accessibility": null,
+          "isOptional": false,
+          "isOverride": false,
+          "readonly": false,
+          "definite": false
+        },
+        {
+          "type": "ClassMethod",
+          "span": {
+            "start": 45,
+            "end": 109,
+            "ctxt": 0
+          },
+          "key": {
+            "type": "Identifier",
+            "span": {
+              "start": 49,
+              "end": 53,
+              "ctxt": 0
+            },
+            "value": "data",
+            "optional": false
+          },
+          "function": {
+            "params": [],
+            "decorators": [],
+            "span": {
+              "start": 45,
+              "end": 109,
+              "ctxt": 0
+            },
+            "body": {
+              "type": "BlockStatement",
+              "span": {
+                "start": 75,
+                "end": 109,
+                "ctxt": 0
+              },
+              "stmts": [
+                {
+                  "type": "ReturnStatement",
+                  "span": {
+                    "start": 85,
+                    "end": 103,
+                    "ctxt": 0
+                  },
+                  "argument": {
+                    "type": "MemberExpression",
+                    "span": {
+                      "start": 92,
+                      "end": 102,
+                      "ctxt": 0
+                    },
+                    "object": {
+                      "type": "ThisExpression",
+                      "span": {
+                        "start": 92,
+                        "end": 96,
+                        "ctxt": 0
+                      }
+                    },
+                    "property": {
+                      "type": "PrivateName",
+                      "span": {
+                        "start": 97,
+                        "end": 102,
+                        "ctxt": 0
+                      },
+                      "id": {
+                        "type": "Identifier",
+                        "span": {
+                          "start": 98,
+                          "end": 102,
+                          "ctxt": 0
+                        },
+                        "value": "data",
+                        "optional": false
+                      }
+                    }
+                  }
+                }
+              ]
+            },
+            "generator": false,
+            "async": false,
+            "typeParameters": null,
+            "returnType": {
+              "type": "TsTypeAnnotation",
+              "span": {
+                "start": 55,
+                "end": 74,
+                "ctxt": 0
+              },
+              "typeAnnotation": {
+                "type": "TsTypeQuery",
+                "span": {
+                  "start": 57,
+                  "end": 74,
+                  "ctxt": 0
+                },
+                "exprName": {
+                  "type": "TsQualifiedName",
+                  "left": {
+                    "type": "Identifier",
+                    "span": {
+                      "start": 64,
+                      "end": 68,
+                      "ctxt": 0
+                    },
+                    "value": "this",
+                    "optional": false
+                  },
+                  "right": {
+                    "type": "PrivateName",
+                    "span": {
+                      "start": 69,
+                      "end": 74,
+                      "ctxt": 0
+                    },
+                    "id": {
+                      "type": "Identifier",
+                      "span": {
+                        "start": 70,
+                        "end": 74,
+                        "ctxt": 0
+                      },
+                      "value": "data",
+                      "optional": false
+                    }
+                  }
+                },
+                "typeArguments": null
+              }
+            }
+          },
+          "kind": "getter",
+          "isStatic": false,
+          "accessibility": null,
+          "isAbstract": false,
+          "isOptional": false,
+          "isOverride": false
+        },
+        {
+          "type": "ClassMethod",
+          "span": {
+            "start": 115,
+            "end": 185,
+            "ctxt": 0
+          },
+          "key": {
+            "type": "Identifier",
+            "span": {
+              "start": 119,
+              "end": 123,
+              "ctxt": 0
+            },
+            "value": "data",
+            "optional": false
+          },
+          "function": {
+            "params": [
+              {
+                "type": "Parameter",
+                "span": {
+                  "start": 124,
+                  "end": 148,
+                  "ctxt": 0
+                },
+                "decorators": [],
+                "pat": {
+                  "type": "Identifier",
+                  "span": {
+                    "start": 124,
+                    "end": 148,
+                    "ctxt": 0
+                  },
+                  "value": "value",
+                  "optional": false,
+                  "typeAnnotation": {
+                    "type": "TsTypeAnnotation",
+                    "span": {
+                      "start": 129,
+                      "end": 148,
+                      "ctxt": 0
+                    },
+                    "typeAnnotation": {
+                      "type": "TsTypeQuery",
+                      "span": {
+                        "start": 131,
+                        "end": 148,
+                        "ctxt": 0
+                      },
+                      "exprName": {
+                        "type": "TsQualifiedName",
+                        "left": {
+                          "type": "Identifier",
+                          "span": {
+                            "start": 138,
+                            "end": 142,
+                            "ctxt": 0
+                          },
+                          "value": "this",
+                          "optional": false
+                        },
+                        "right": {
+                          "type": "PrivateName",
+                          "span": {
+                            "start": 143,
+                            "end": 148,
+                            "ctxt": 0
+                          },
+                          "id": {
+                            "type": "Identifier",
+                            "span": {
+                              "start": 144,
+                              "end": 148,
+                              "ctxt": 0
+                            },
+                            "value": "data",
+                            "optional": false
+                          }
+                        }
+                      },
+                      "typeArguments": null
+                    }
+                  }
+                }
+              }
+            ],
+            "decorators": [],
+            "span": {
+              "start": 115,
+              "end": 185,
+              "ctxt": 0
+            },
+            "body": {
+              "type": "BlockStatement",
+              "span": {
+                "start": 150,
+                "end": 185,
+                "ctxt": 0
+              },
+              "stmts": [
+                {
+                  "type": "ExpressionStatement",
+                  "span": {
+                    "start": 160,
+                    "end": 179,
+                    "ctxt": 0
+                  },
+                  "expression": {
+                    "type": "AssignmentExpression",
+                    "span": {
+                      "start": 160,
+                      "end": 178,
+                      "ctxt": 0
+                    },
+                    "operator": "=",
+                    "left": {
+                      "type": "MemberExpression",
+                      "span": {
+                        "start": 160,
+                        "end": 170,
+                        "ctxt": 0
+                      },
+                      "object": {
+                        "type": "ThisExpression",
+                        "span": {
+                          "start": 160,
+                          "end": 164,
+                          "ctxt": 0
+                        }
+                      },
+                      "property": {
+                        "type": "PrivateName",
+                        "span": {
+                          "start": 165,
+                          "end": 170,
+                          "ctxt": 0
+                        },
+                        "id": {
+                          "type": "Identifier",
+                          "span": {
+                            "start": 166,
+                            "end": 170,
+                            "ctxt": 0
+                          },
+                          "value": "data",
+                          "optional": false
+                        }
+                      }
+                    },
+                    "right": {
+                      "type": "Identifier",
+                      "span": {
+                        "start": 173,
+                        "end": 178,
+                        "ctxt": 0
+                      },
+                      "value": "value",
+                      "optional": false
+                    }
+                  }
+                }
+              ]
+            },
+            "generator": false,
+            "async": false,
+            "typeParameters": null,
+            "returnType": null
+          },
+          "kind": "setter",
+          "isStatic": false,
+          "accessibility": null,
+          "isAbstract": false,
+          "isOptional": false,
+          "isOverride": false
+        }
+      ],
+      "superClass": null,
+      "isAbstract": false,
+      "typeParams": null,
+      "superTypeParams": null,
+      "implements": []
+    }
+  ],
+  "interpreter": null
+}
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/1/input.ts b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/1/input.ts
new file mode 100644
index 0000000000..95d8b82a0a
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/1/input.ts
@@ -0,0 +1,8 @@
+class C {
+    #a = 'a';
+
+    constructor() {
+        const a: typeof this.#a = ''; // Ok
+        const b: typeof this.#a = 1;  // Error
+    }
+}
\ No newline at end of file
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/1/output.js b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/1/output.js
new file mode 100644
index 0000000000..c4a8013669
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/1/output.js
@@ -0,0 +1,11 @@
+var _a = /*#__PURE__*/ new WeakMap();
+class C {
+    constructor(){
+        _classPrivateFieldInit(this, _a, {
+            writable: true,
+            value: 'a'
+        });
+        const a = ''; // Ok
+        const b = 1; // Error
+    }
+}
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/2/input.ts b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/2/input.ts
new file mode 100644
index 0000000000..028caeaf4b
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/2/input.ts
@@ -0,0 +1,11 @@
+class Container {
+    #data = "hello!";
+
+    get data(): typeof this.#data {
+        return this.#data;
+    }
+
+    set data(value: typeof this.#data) {
+        this.#data = value;
+    }
+}
\ No newline at end of file
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/2/output.js b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/2/output.js
new file mode 100644
index 0000000000..1919de0084
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-4287/2/output.js
@@ -0,0 +1,15 @@
+var _data = /*#__PURE__*/ new WeakMap();
+class Container {
+    get data() {
+        return _classPrivateFieldGet(this, _data);
+    }
+    set data(value) {
+        _classPrivateFieldSet(this, _data, value);
+    }
+    constructor(){
+        _classPrivateFieldInit(this, _data, {
+            writable: true,
+            value: "hello!"
+        });
+    }
+}