From 1be83015ec08c43b5da92d4cdc3e34bb0222f401 Mon Sep 17 00:00:00 2001 From: Youssef Mansour Date: Fri, 28 Aug 2026 07:53:56 +0300 Subject: [PATCH] Fix importHelpers incorrectly requiring tslib for native #private class members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The check for private field helpers was incorrectly gated by ClassAndClassElementDecorators (pinned to ESNext), causing all dated targets (ES2022–ES2025) to require tslib even though native private fields are supported since ES2022 and no helper is ever emitted. Fixed in four locations: - checkPropertyAccessExpressionOrQualifiedName (private field get/set) - checkInExpression (private field 'in' checks) - setNodeLinksForPrivateIdentifierScope - getFirstTransformableStaticClassElement Fixes #63728 --- tsc/internal/checker/checker.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index fb3c33c01b814..5d569b26ab2ee 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -10147,7 +10147,9 @@ func (c *Checker) getFirstTransformableStaticClassElement(node *ast.Node) *ast.N willTransformStaticElementsOfDecoratedClass := !c.legacyDecorators && c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators && ast.ClassOrConstructorParameterIsDecorated(false, node) - willTransformPrivateElementsOrClassStaticBlocks := c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks || c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators + // Private elements and class static blocks only need transformation before ES2022. + // Decorators (ClassAndClassElementDecorators) are a separate feature and should not gate private element transformation. + willTransformPrivateElementsOrClassStaticBlocks := c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks willTransformInitializers := !c.emitStandardClassFields if willTransformStaticElementsOfDecoratedClass || willTransformPrivateElementsOrClassStaticBlocks { for _, member := range node.Members() { @@ -10618,8 +10620,11 @@ func (c *Checker) needCollisionCheckForIdentifier(node *ast.Node, identifier *as func (c *Checker) setNodeLinksForPrivateIdentifierScope(node *ast.Node) { if name := node.Name(); ast.IsPrivateIdentifier(name) { + // Private field helper is only needed when: + // 1. Target is before ES2022 (when native private fields were introduced) + // 2. useDefineForClassFields is false (legacy class field semantics) + // Decorators (ClassAndClassElementDecorators) are a separate feature and should not gate private field helpers. if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks || - c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators || !c.compilerOptions.GetUseDefineForClassFields() { for lexicalScope := ast.GetEnclosingBlockScopeContainer(node); lexicalScope != nil; lexicalScope = ast.GetEnclosingBlockScopeContainer(lexicalScope) { c.nodeLinks.Get(lexicalScope).flags |= NodeCheckFlagsContainsClassWithPrivateIdentifiers @@ -11358,8 +11363,11 @@ func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, l isAnyLike := IsTypeAny(apparentType) || apparentType == c.silentNeverType var prop *ast.Symbol if ast.IsPrivateIdentifier(right) { + // Private field helper is only needed when: + // 1. Target is before ES2022 (when native private fields were introduced) + // 2. useDefineForClassFields is false (legacy class field semantics) + // Decorators (ClassAndClassElementDecorators) are a separate feature and should not gate private field helpers. if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks || - c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators || !c.compilerOptions.GetUseDefineForClassFields() { if assignmentKind != AssignmentKindNone { c.checkExternalEmitHelpers(node, ExternalEmitHelpersClassPrivateFieldSet) @@ -13173,8 +13181,11 @@ func (c *Checker) checkInExpression(left *ast.Expression, right *ast.Expression, return c.silentNeverType } if ast.IsPrivateIdentifier(left) { + // Private field helper is only needed when: + // 1. Target is before ES2022 (when native private fields were introduced) + // 2. useDefineForClassFields is false (legacy class field semantics) + // Decorators (ClassAndClassElementDecorators) are a separate feature and should not gate private field helpers. if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks || - c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators || !c.compilerOptions.GetUseDefineForClassFields() { c.checkExternalEmitHelpers(left, ExternalEmitHelpersClassPrivateFieldIn) }