From 6f43da9408c76160762bf266eae8438ea9e9a048 Mon Sep 17 00:00:00 2001 From: Andrew Stegmaier Date: Thu, 6 Aug 2026 16:45:57 -0400 Subject: [PATCH 1/2] Fix false-positive TS2354 for native private class field access at dated targets Port of the fix for microsoft/TypeScript#63728 to this codebase, since the checks in question (setNodeLinksForPrivateIdentifierScope, checkPropertyAccessExpressionOrQualifiedName, checkInExpression in internal/checker/checker.go) were carried over unchanged from the classic compiler's checker.ts by the private-field-helpers porting work, and exhibit the identical bug. With importHelpers: true and a dated target (ES2022 through ES2025), the checks required tslib for private-identifier access via: c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks || c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators || !c.compilerOptions.GetUseDefineForClassFields() Since decorators have never been assigned a dated ECMAScript edition, ClassAndClassElementDecorators is pinned to the ESNext sentinel, so the middle clause is true for every dated target unconditionally, regardless of whether the file uses decorators at all. The actual emitter (estransforms/classfields.go's shouldTransformPrivateElementsOrClassStaticBlocks) only checks languageVersion < ES2022, with no decorator-related gating, so this required tslib to be resolvable even though the emitted JS for plain private field/method/accessor access never references it. The !GetUseDefineForClassFields() clause has the same problem: private fields are always emitted using 'define' semantics regardless of that option (it only affects public fields), so it also doesn't correspond to any real difference in whether tslib is needed - verified by emitting with --useDefineForClassFields false --target es2022 and confirming the output is fully native with zero tslib references. A fourth call site with the same ClassAndClassElementDecorators check, inside getFirstTransformableStaticClassElement, is left unchanged: it's used only to decide whether a *decorated* class needs the __setFunctionName helper for hoisting its static private/static-block elements, which is a genuine, verified coupling between decorators and private statics (confirmed by emitting a decorated class with a static private method and observing __setFunctionName really is called). Note on scope: per CONTRIBUTING.md, this repo is currently only accepting 6.0/7.0-difference or crash fixes. This bug is not a 6.0/7.0 difference - it reproduces identically in the classic compiler (see microsoft/TypeScript#63728, and companion fix microsoft/TypeScript#63729) and this port, since the checker logic was ported over unchanged. Submitting here anyway since this is shared logic both compilers carry, and the fix keeps their behavior in sync; happy to close this if it's considered out-of-scope for the 7.0 bridge period. Related to microsoft/TypeScript#63728. --- Disclosure: This PR was authored with AI assistance (GitHub Copilot CLI), directed by a specific human operator (astegmaier) investigating and fixing this one specific, previously-filed issue. It is not part of a bulk or queue-driven workflow, and I will personally shepherd it through review and respond to feedback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- internal/checker/checker.go | 12 +- ...elpersNoHelpersForPrivateFieldsAtES2022.js | 60 ++++++++++ ...sNoHelpersForPrivateFieldsAtES2022.symbols | 88 +++++++++++++++ ...ersNoHelpersForPrivateFieldsAtES2022.types | 103 ++++++++++++++++++ ...ldsAtES2022UseDefineForClassFieldsFalse.js | 36 ++++++ ...ES2022UseDefineForClassFieldsFalse.symbols | 47 ++++++++ ...AtES2022UseDefineForClassFieldsFalse.types | 54 +++++++++ ...elpersNoHelpersForPrivateFieldsAtES2022.ts | 28 +++++ ...ldsAtES2022UseDefineForClassFieldsFalse.ts | 18 +++ 9 files changed, 437 insertions(+), 9 deletions(-) create mode 100644 testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.js create mode 100644 testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.symbols create mode 100644 testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.types create mode 100644 testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.js create mode 100644 testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.symbols create mode 100644 testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.types create mode 100644 testdata/tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.ts create mode 100644 testdata/tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.ts diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 0a59c319876..8107f03835b 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -10531,9 +10531,7 @@ func (c *Checker) needCollisionCheckForIdentifier(node *ast.Node, identifier *as func (c *Checker) setNodeLinksForPrivateIdentifierScope(node *ast.Node) { if name := node.Name(); ast.IsPrivateIdentifier(name) { - if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks || - c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators || - !c.compilerOptions.GetUseDefineForClassFields() { + if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks { for lexicalScope := ast.GetEnclosingBlockScopeContainer(node); lexicalScope != nil; lexicalScope = ast.GetEnclosingBlockScopeContainer(lexicalScope) { c.nodeLinks.Get(lexicalScope).flags |= NodeCheckFlagsContainsClassWithPrivateIdentifiers } @@ -11271,9 +11269,7 @@ func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, l isAnyLike := IsTypeAny(apparentType) || apparentType == c.silentNeverType var prop *ast.Symbol if ast.IsPrivateIdentifier(right) { - if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks || - c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators || - !c.compilerOptions.GetUseDefineForClassFields() { + if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks { if assignmentKind != AssignmentKindNone { c.checkExternalEmitHelpers(node, ExternalEmitHelpersClassPrivateFieldSet) } @@ -13084,9 +13080,7 @@ func (c *Checker) checkInExpression(left *ast.Expression, right *ast.Expression, return c.silentNeverType } if ast.IsPrivateIdentifier(left) { - if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks || - c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators || - !c.compilerOptions.GetUseDefineForClassFields() { + if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks { c.checkExternalEmitHelpers(left, ExternalEmitHelpersClassPrivateFieldIn) } // Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type diff --git a/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.js b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.js new file mode 100644 index 00000000000..f700dc1d0a9 --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.js @@ -0,0 +1,60 @@ +//// [tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.ts] //// + +//// [main.ts] +export class Foo { + #field = true; + static #staticField = true; + #method() {} + static #staticMethod() {} + get #accessor() { return this.#field; } + set #accessor(v: boolean) { this.#field = v; } + accessor #autoAccessor = true; + static accessor #staticAutoAccessor = true; + static { + Foo.#staticField = true; + } + f() { + this.#field = this.#field; + this.#method(); + Foo.#staticField = Foo.#staticField; + Foo.#staticMethod(); + this.#accessor = this.#accessor; + this.#autoAccessor = this.#autoAccessor; + Foo.#staticAutoAccessor = Foo.#staticAutoAccessor; + #field in this; + } +} + + +//// [main.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Foo = void 0; +class Foo { + #field = true; + static #staticField = true; + #method() { } + static #staticMethod() { } + get #accessor() { return this.#field; } + set #accessor(v) { this.#field = v; } + #autoAccessor_accessor_storage = true; + get #autoAccessor() { return this.#autoAccessor_accessor_storage; } + set #autoAccessor(value) { this.#autoAccessor_accessor_storage = value; } + static #staticAutoAccessor_accessor_storage = true; + static get #staticAutoAccessor() { return Foo.#staticAutoAccessor_accessor_storage; } + static set #staticAutoAccessor(value) { Foo.#staticAutoAccessor_accessor_storage = value; } + static { + Foo.#staticField = true; + } + f() { + this.#field = this.#field; + this.#method(); + Foo.#staticField = Foo.#staticField; + Foo.#staticMethod(); + this.#accessor = this.#accessor; + this.#autoAccessor = this.#autoAccessor; + Foo.#staticAutoAccessor = Foo.#staticAutoAccessor; + #field in this; + } +} +exports.Foo = Foo; diff --git a/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.symbols b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.symbols new file mode 100644 index 00000000000..374cd2d4702 --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.symbols @@ -0,0 +1,88 @@ +//// [tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.ts] //// + +=== main.ts === +export class Foo { +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) + + #field = true; +>#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) + + static #staticField = true; +>#staticField : Symbol(Foo.#staticField, Decl(main.ts, 1, 18)) + + #method() {} +>#method : Symbol(Foo.#method, Decl(main.ts, 2, 31)) + + static #staticMethod() {} +>#staticMethod : Symbol(Foo.#staticMethod, Decl(main.ts, 3, 16)) + + get #accessor() { return this.#field; } +>#accessor : Symbol(Foo.#accessor, Decl(main.ts, 4, 29), Decl(main.ts, 5, 43)) +>this.#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) + + set #accessor(v: boolean) { this.#field = v; } +>#accessor : Symbol(Foo.#accessor, Decl(main.ts, 4, 29), Decl(main.ts, 5, 43)) +>v : Symbol(v, Decl(main.ts, 6, 18)) +>this.#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) +>v : Symbol(v, Decl(main.ts, 6, 18)) + + accessor #autoAccessor = true; +>#autoAccessor : Symbol(Foo.#autoAccessor, Decl(main.ts, 6, 50)) + + static accessor #staticAutoAccessor = true; +>#staticAutoAccessor : Symbol(Foo.#staticAutoAccessor, Decl(main.ts, 7, 34)) + + static { + Foo.#staticField = true; +>Foo.#staticField : Symbol(Foo.#staticField, Decl(main.ts, 1, 18)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) + } + f() { +>f : Symbol(Foo.f, Decl(main.ts, 11, 5)) + + this.#field = this.#field; +>this.#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) +>this.#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) + + this.#method(); +>this.#method : Symbol(Foo.#method, Decl(main.ts, 2, 31)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) + + Foo.#staticField = Foo.#staticField; +>Foo.#staticField : Symbol(Foo.#staticField, Decl(main.ts, 1, 18)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) +>Foo.#staticField : Symbol(Foo.#staticField, Decl(main.ts, 1, 18)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) + + Foo.#staticMethod(); +>Foo.#staticMethod : Symbol(Foo.#staticMethod, Decl(main.ts, 3, 16)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) + + this.#accessor = this.#accessor; +>this.#accessor : Symbol(Foo.#accessor, Decl(main.ts, 4, 29), Decl(main.ts, 5, 43)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) +>this.#accessor : Symbol(Foo.#accessor, Decl(main.ts, 4, 29), Decl(main.ts, 5, 43)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) + + this.#autoAccessor = this.#autoAccessor; +>this.#autoAccessor : Symbol(Foo.#autoAccessor, Decl(main.ts, 6, 50)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) +>this.#autoAccessor : Symbol(Foo.#autoAccessor, Decl(main.ts, 6, 50)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) + + Foo.#staticAutoAccessor = Foo.#staticAutoAccessor; +>Foo.#staticAutoAccessor : Symbol(Foo.#staticAutoAccessor, Decl(main.ts, 7, 34)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) +>Foo.#staticAutoAccessor : Symbol(Foo.#staticAutoAccessor, Decl(main.ts, 7, 34)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) + + #field in this; +>#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) + } +} + diff --git a/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.types b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.types new file mode 100644 index 00000000000..3562c783c32 --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.types @@ -0,0 +1,103 @@ +//// [tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.ts] //// + +=== main.ts === +export class Foo { +>Foo : Foo + + #field = true; +>#field : boolean +>true : true + + static #staticField = true; +>#staticField : boolean +>true : true + + #method() {} +>#method : () => void + + static #staticMethod() {} +>#staticMethod : () => void + + get #accessor() { return this.#field; } +>#accessor : boolean +>this.#field : boolean +>this : this + + set #accessor(v: boolean) { this.#field = v; } +>#accessor : boolean +>v : boolean +>this.#field = v : boolean +>this.#field : boolean +>this : this +>v : boolean + + accessor #autoAccessor = true; +>#autoAccessor : boolean +>true : true + + static accessor #staticAutoAccessor = true; +>#staticAutoAccessor : boolean +>true : true + + static { + Foo.#staticField = true; +>Foo.#staticField = true : true +>Foo.#staticField : boolean +>Foo : typeof Foo +>true : true + } + f() { +>f : () => void + + this.#field = this.#field; +>this.#field = this.#field : boolean +>this.#field : boolean +>this : this +>this.#field : boolean +>this : this + + this.#method(); +>this.#method() : void +>this.#method : () => void +>this : this + + Foo.#staticField = Foo.#staticField; +>Foo.#staticField = Foo.#staticField : boolean +>Foo.#staticField : boolean +>Foo : typeof Foo +>Foo.#staticField : boolean +>Foo : typeof Foo + + Foo.#staticMethod(); +>Foo.#staticMethod() : void +>Foo.#staticMethod : () => void +>Foo : typeof Foo + + this.#accessor = this.#accessor; +>this.#accessor = this.#accessor : boolean +>this.#accessor : boolean +>this : this +>this.#accessor : boolean +>this : this + + this.#autoAccessor = this.#autoAccessor; +>this.#autoAccessor = this.#autoAccessor : boolean +>this.#autoAccessor : boolean +>this : this +>this.#autoAccessor : boolean +>this : this + + Foo.#staticAutoAccessor = Foo.#staticAutoAccessor; +>Foo.#staticAutoAccessor = Foo.#staticAutoAccessor : boolean +>Foo.#staticAutoAccessor : boolean +>Foo : typeof Foo +>Foo.#staticAutoAccessor : boolean +>Foo : typeof Foo + + #field in this; +>#field in this : boolean +>#field : any +>this : this + } +} + diff --git a/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.js b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.js new file mode 100644 index 00000000000..852bd21f7d1 --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.js @@ -0,0 +1,36 @@ +//// [tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.ts] //// + +//// [main.ts] +export class Foo { + #field = true; + static #staticField = true; + #method() {} + static #staticMethod() {} + f() { + this.#field = this.#field; + this.#method(); + Foo.#staticField = Foo.#staticField; + Foo.#staticMethod(); + #field in this; + } +} + + +//// [main.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Foo = void 0; +class Foo { + #field = true; + static #staticField = true; + #method() { } + static #staticMethod() { } + f() { + this.#field = this.#field; + this.#method(); + Foo.#staticField = Foo.#staticField; + Foo.#staticMethod(); + #field in this; + } +} +exports.Foo = Foo; diff --git a/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.symbols b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.symbols new file mode 100644 index 00000000000..a25ba762982 --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.symbols @@ -0,0 +1,47 @@ +//// [tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.ts] //// + +=== main.ts === +export class Foo { +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) + + #field = true; +>#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) + + static #staticField = true; +>#staticField : Symbol(Foo.#staticField, Decl(main.ts, 1, 18)) + + #method() {} +>#method : Symbol(Foo.#method, Decl(main.ts, 2, 31)) + + static #staticMethod() {} +>#staticMethod : Symbol(Foo.#staticMethod, Decl(main.ts, 3, 16)) + + f() { +>f : Symbol(Foo.f, Decl(main.ts, 4, 29)) + + this.#field = this.#field; +>this.#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) +>this.#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) + + this.#method(); +>this.#method : Symbol(Foo.#method, Decl(main.ts, 2, 31)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) + + Foo.#staticField = Foo.#staticField; +>Foo.#staticField : Symbol(Foo.#staticField, Decl(main.ts, 1, 18)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) +>Foo.#staticField : Symbol(Foo.#staticField, Decl(main.ts, 1, 18)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) + + Foo.#staticMethod(); +>Foo.#staticMethod : Symbol(Foo.#staticMethod, Decl(main.ts, 3, 16)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) + + #field in this; +>#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) + } +} + diff --git a/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.types b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.types new file mode 100644 index 00000000000..eb5d0654fd7 --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.types @@ -0,0 +1,54 @@ +//// [tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.ts] //// + +=== main.ts === +export class Foo { +>Foo : Foo + + #field = true; +>#field : boolean +>true : true + + static #staticField = true; +>#staticField : boolean +>true : true + + #method() {} +>#method : () => void + + static #staticMethod() {} +>#staticMethod : () => void + + f() { +>f : () => void + + this.#field = this.#field; +>this.#field = this.#field : boolean +>this.#field : boolean +>this : this +>this.#field : boolean +>this : this + + this.#method(); +>this.#method() : void +>this.#method : () => void +>this : this + + Foo.#staticField = Foo.#staticField; +>Foo.#staticField = Foo.#staticField : boolean +>Foo.#staticField : boolean +>Foo : typeof Foo +>Foo.#staticField : boolean +>Foo : typeof Foo + + Foo.#staticMethod(); +>Foo.#staticMethod() : void +>Foo.#staticMethod : () => void +>Foo : typeof Foo + + #field in this; +>#field in this : boolean +>#field : any +>this : this + } +} + diff --git a/testdata/tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.ts b/testdata/tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.ts new file mode 100644 index 00000000000..1a33ef6f52a --- /dev/null +++ b/testdata/tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022.ts @@ -0,0 +1,28 @@ +// @importHelpers: true +// @target: es2022 +// @module: commonjs +// @lib: esnext +// @filename: main.ts +export class Foo { + #field = true; + static #staticField = true; + #method() {} + static #staticMethod() {} + get #accessor() { return this.#field; } + set #accessor(v: boolean) { this.#field = v; } + accessor #autoAccessor = true; + static accessor #staticAutoAccessor = true; + static { + Foo.#staticField = true; + } + f() { + this.#field = this.#field; + this.#method(); + Foo.#staticField = Foo.#staticField; + Foo.#staticMethod(); + this.#accessor = this.#accessor; + this.#autoAccessor = this.#autoAccessor; + Foo.#staticAutoAccessor = Foo.#staticAutoAccessor; + #field in this; + } +} diff --git a/testdata/tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.ts b/testdata/tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.ts new file mode 100644 index 00000000000..9d65f9e9abc --- /dev/null +++ b/testdata/tests/cases/compiler/importHelpersNoHelpersForPrivateFieldsAtES2022UseDefineForClassFieldsFalse.ts @@ -0,0 +1,18 @@ +// @importHelpers: true +// @target: es2022 +// @useDefineForClassFields: false +// @module: commonjs +// @filename: main.ts +export class Foo { + #field = true; + static #staticField = true; + #method() {} + static #staticMethod() {} + f() { + this.#field = this.#field; + this.#method(); + Foo.#staticField = Foo.#staticField; + Foo.#staticMethod(); + #field in this; + } +} From a041b0472f6fa4be771869885fd994324d2ebc85 Mon Sep 17 00:00:00 2001 From: Andrew Stegmaier Date: Fri, 7 Aug 2026 18:00:21 -0400 Subject: [PATCH 2/2] Restore tslib helper validation for decorator-hoisted static private elements Follow-up to the earlier fix in this branch for microsoft/TypeScript#63728: that fix simplified checkPropertyAccessExpressionOrQualifiedName, checkInExpression, and setNodeLinksForPrivateIdentifierScope to only require tslib's classPrivateField* helpers below ES2022, since the actual emitter doesn't need them for plain private field/method/accessor access at ES2022+. However, that simplification missed one real exception, caught by an automated PR review: when a class has a native (non-legacy) class decorator AND at least one static private/auto-accessor element, decorator lowering (estransforms/esdecorator.go's hasStaticPrivateClassElements / shouldTransformPrivateStaticElementsInClass) hoists ALL of that class's static private/auto-accessor elements out of the class body into a wrapping closure. That hoisting forces accesses to those specific elements to use the classPrivateFieldGet/Set/In helpers from tslib, even at ES2022+ - so the checker needs to keep validating tslib exports them in this narrow case. Instance private fields in the same decorated class are unaffected and stay fully native. Adds isStaticPrivateElementOfDecoratedClass, mirroring newESDecoratorTransformer's exact skip condition (decorator lowering only doesn't happen for !legacyDecorators when target >= ESNext with useDefineForClassFields true), and ORs it into the two checks that validate these specific helpers (checkPropertyAccessExpressionOrQualifiedName, checkInExpression). setNodeLinksForPrivateIdentifierScope is intentionally left alone: its NodeCheckFlagsContainsClassWithPrivateIdentifiers flag has exactly one consumer (checkWeakMapSetCollision), which independently gates on languageVersion <= ES2021, making any change there dead code for the ES2022+ scenario this follow-up addresses. Verified with two independent review passes plus manual testing: - Confirmed via actual JS emission that decorated classes' static private field/method/accessor access and '#x in obj' checks call tslib_1.__classPrivateFieldGet/Set/In, while instance private field access in the same decorated class stays fully native. - Confirmed the fix correctly requires the helpers for static private fields, methods, get/set accessors, and auto-accessors in decorated classes; correctly leaves instance fields, non-decorated classes, and legacy (experimentalDecorators) classes unaffected; and correctly follows the useDefineForClassFields/ESNext edge case (decorator lowering is skipped, and no helpers are needed, only at target >= ESNext with useDefineForClassFields true). - Ran the full Go test suite (go test ./...) and the submodule conformance suite filtered to privateName|esDecorators|importHelpers|classField|classStaticBlock - all passing. - hereby lint and hereby format are clean. - Investigated two additional discrepancies surfaced during review (a missing TS2354 for decorator-only helpers, and an emit panic for static auto-accessors, both specific to target: esnext with useDefineForClassFields: false) and confirmed both are pre-existing in main, unrelated to any change in this branch, and out of scope for this fix. Adds three new compiler tests: - importHelpersRequiredForDecoratedStaticPrivateElements.ts: a decorated class with an instance field plus static field/method/get-set-accessor/auto-accessor and a static '#x in obj' check, with a tslib stub missing the classPrivateField* helpers - confirms exactly the expected 3 errors (one per helper) anchored at the static accesses, with zero errors for the instance field. - importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.ts: same shape at target: esnext with useDefineForClassFields: false, confirming decorator lowering (and thus the helper requirement) still applies in that specific target/option combination. - importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.ts: the same shape at target: esnext with the default useDefineForClassFields: true, confirming zero errors and fully native emission when decorator lowering is skipped entirely. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- internal/checker/checker.go | 24 +++- ...rDecoratedStaticPrivateElementsAtESNext.js | 32 +++++ ...ratedStaticPrivateElementsAtESNext.symbols | 27 ++++ ...coratedStaticPrivateElementsAtESNext.types | 28 +++++ ...rDecoratedStaticPrivateElements.errors.txt | 54 ++++++++ ...quiredForDecoratedStaticPrivateElements.js | 103 +++++++++++++++ ...dForDecoratedStaticPrivateElements.symbols | 108 ++++++++++++++++ ...redForDecoratedStaticPrivateElements.types | 117 ++++++++++++++++++ ...ntsUseDefineForClassFieldsFalse.errors.txt | 32 +++++ ...ateElementsUseDefineForClassFieldsFalse.js | 71 +++++++++++ ...ementsUseDefineForClassFieldsFalse.symbols | 62 ++++++++++ ...ElementsUseDefineForClassFieldsFalse.types | 64 ++++++++++ ...rDecoratedStaticPrivateElementsAtESNext.ts | 16 +++ ...quiredForDecoratedStaticPrivateElements.ts | 45 +++++++ ...ateElementsUseDefineForClassFieldsFalse.ts | 30 +++++ 15 files changed, 809 insertions(+), 4 deletions(-) create mode 100644 testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.js create mode 100644 testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.symbols create mode 100644 testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.types create mode 100644 testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.errors.txt create mode 100644 testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.js create mode 100644 testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.symbols create mode 100644 testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.types create mode 100644 testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.errors.txt create mode 100644 testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.js create mode 100644 testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.symbols create mode 100644 testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.types create mode 100644 testdata/tests/cases/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.ts create mode 100644 testdata/tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.ts create mode 100644 testdata/tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.ts diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 8107f03835b..6a7cf797903 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -11269,7 +11269,8 @@ func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, l isAnyLike := IsTypeAny(apparentType) || apparentType == c.silentNeverType var prop *ast.Symbol if ast.IsPrivateIdentifier(right) { - if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks { + lexicallyScopedSymbol := c.lookupSymbolForPrivateIdentifierDeclaration(right.Text(), right) + if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks || c.isStaticPrivateElementOfDecoratedClass(lexicallyScopedSymbol) { if assignmentKind != AssignmentKindNone { c.checkExternalEmitHelpers(node, ExternalEmitHelpersClassPrivateFieldSet) } @@ -11277,7 +11278,6 @@ func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, l c.checkExternalEmitHelpers(node, ExternalEmitHelpersClassPrivateFieldGet) } } - lexicallyScopedSymbol := c.lookupSymbolForPrivateIdentifierDeclaration(right.Text(), right) if assignmentKind != AssignmentKindNone && lexicallyScopedSymbol != nil && lexicallyScopedSymbol.ValueDeclaration != nil && ast.IsMethodDeclaration(lexicallyScopedSymbol.ValueDeclaration) { c.grammarErrorOnNode(right, diagnostics.Cannot_assign_to_private_method_0_Private_methods_are_not_writable, right.Text()) } @@ -11492,6 +11492,21 @@ func (c *Checker) getPrivateIdentifierPropertyOfType(leftType *Type, lexicallySc return c.getPropertyOfType(leftType, lexicallyScopedIdentifier.Name) } +// isStaticPrivateElementOfDecoratedClass reports whether symbol is a static private class element +// (field, method, accessor, or auto-accessor) declared in a class whose native (non-legacy) decorators +// will actually be lowered. Decorator lowering hoists a decorated class's static private elements out +// of the class body into a wrapping closure, which forces accesses to them to use the +// classPrivateField* emit helpers even at targets that otherwise support private fields natively - see +// esDecoratorTransformer's hasStaticPrivateClassElements/shouldTransformPrivateStaticElementsInClass. +func (c *Checker) isStaticPrivateElementOfDecoratedClass(symbol *ast.Symbol) bool { + if symbol == nil || symbol.ValueDeclaration == nil || c.legacyDecorators || !ast.HasStaticModifier(symbol.ValueDeclaration) { + return false + } + class := ast.GetContainingClass(symbol.ValueDeclaration) + return class != nil && ast.HasDecorators(class) && + (c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators || !c.compilerOptions.GetUseDefineForClassFields()) +} + func (c *Checker) checkPrivateIdentifierPropertyAccess(leftType *Type, right *ast.Node, lexicallyScopedIdentifier *ast.Symbol) bool { // Either the identifier could not be looked up in the lexical scope OR the lexically scoped identifier did not exist on the type. // Find a private identifier with the same description on the type. @@ -13080,12 +13095,13 @@ func (c *Checker) checkInExpression(left *ast.Expression, right *ast.Expression, return c.silentNeverType } if ast.IsPrivateIdentifier(left) { - if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks { + lexicallyScopedSymbol := c.symbolNodeLinks.Get(left).resolvedSymbol + if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks || c.isStaticPrivateElementOfDecoratedClass(lexicallyScopedSymbol) { c.checkExternalEmitHelpers(left, ExternalEmitHelpersClassPrivateFieldIn) } // Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type // which provides us with the opportunity to emit more detailed errors - if c.symbolNodeLinks.Get(left).resolvedSymbol == nil && ast.GetContainingClass(left) != nil { + if lexicallyScopedSymbol == nil && ast.GetContainingClass(left) != nil { isUncheckedJS := c.isUncheckedJSSuggestion(left, rightType.symbol, true /*excludeClasses*/) c.reportNonexistentProperty(left, rightType, isUncheckedJS) } diff --git a/testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.js b/testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.js new file mode 100644 index 00000000000..488baa9b1a4 --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.js @@ -0,0 +1,32 @@ +//// [tests/cases/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.ts] //// + +//// [main.ts] +export declare var dec: any; + +// At target: esnext with the default useDefineForClassFields: true, native decorators need no +// transform at all (see newESDecoratorTransformer's skip condition), so even a decorated class's +// static private elements stay fully native and never need the classPrivateField* helpers. +@dec +export class Foo { + static #staticField = 1; + static getStaticField() { + return Foo.#staticField; + } +} + + +//// [main.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Foo = void 0; +// At target: esnext with the default useDefineForClassFields: true, native decorators need no +// transform at all (see newESDecoratorTransformer's skip condition), so even a decorated class's +// static private elements stay fully native and never need the classPrivateField* helpers. +@exports.dec +class Foo { + static #staticField = 1; + static getStaticField() { + return Foo.#staticField; + } +} +exports.Foo = Foo; diff --git a/testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.symbols b/testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.symbols new file mode 100644 index 00000000000..61fc80a0ec3 --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.symbols @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.ts] //// + +=== main.ts === +export declare var dec: any; +>dec : Symbol(dec, Decl(main.ts, 0, 18)) + +// At target: esnext with the default useDefineForClassFields: true, native decorators need no +// transform at all (see newESDecoratorTransformer's skip condition), so even a decorated class's +// static private elements stay fully native and never need the classPrivateField* helpers. +@dec +>dec : Symbol(dec, Decl(main.ts, 0, 18)) + +export class Foo { +>Foo : Symbol(Foo, Decl(main.ts, 0, 28)) + + static #staticField = 1; +>#staticField : Symbol(Foo.#staticField, Decl(main.ts, 6, 18)) + + static getStaticField() { +>getStaticField : Symbol(Foo.getStaticField, Decl(main.ts, 7, 28)) + + return Foo.#staticField; +>Foo.#staticField : Symbol(Foo.#staticField, Decl(main.ts, 6, 18)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 28)) + } +} + diff --git a/testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.types b/testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.types new file mode 100644 index 00000000000..24107615a20 --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.types @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.ts] //// + +=== main.ts === +export declare var dec: any; +>dec : any + +// At target: esnext with the default useDefineForClassFields: true, native decorators need no +// transform at all (see newESDecoratorTransformer's skip condition), so even a decorated class's +// static private elements stay fully native and never need the classPrivateField* helpers. +@dec +>dec : any + +export class Foo { +>Foo : Foo + + static #staticField = 1; +>#staticField : number +>1 : 1 + + static getStaticField() { +>getStaticField : () => number + + return Foo.#staticField; +>Foo.#staticField : number +>Foo : typeof Foo + } +} + diff --git a/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.errors.txt b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.errors.txt new file mode 100644 index 00000000000..7babac6827a --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.errors.txt @@ -0,0 +1,54 @@ +main.ts(20,16): error TS2343: This syntax requires an imported helper named '__classPrivateFieldGet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. +main.ts(26,9): error TS2343: This syntax requires an imported helper named '__classPrivateFieldSet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. +main.ts(32,16): error TS2343: This syntax requires an imported helper named '__classPrivateFieldIn' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. + + +==== main.ts (3 errors) ==== + export declare var dec: any; + + // Class decorators hoist a class's static private/auto-accessor elements out of the class body, so + // accessing them still requires the classPrivateField* helpers even at targets (like es2022) that + // otherwise support private fields natively. Instance private fields are unaffected by this and stay + // fully native even in a decorated class. + @dec + export class Foo { + #instanceField = 1; + static #staticField = 1; + static #staticMethod() { return 1; } + static get #staticAccessor() { return 1; } + static set #staticAccessor(v: number) {} + static accessor #staticAutoAccessor = 1; + + getInstanceField() { + return this.#instanceField; + } + static getStaticField() { + return Foo.#staticField; + ~~~~~~~~~~~~~~~~ +!!! error TS2343: This syntax requires an imported helper named '__classPrivateFieldGet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. + } + static callStaticMethod() { + return Foo.#staticMethod(); + } + static useStaticAccessor() { + Foo.#staticAccessor = Foo.#staticAccessor; + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2343: This syntax requires an imported helper named '__classPrivateFieldSet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. + } + static useStaticAutoAccessor() { + Foo.#staticAutoAccessor = Foo.#staticAutoAccessor; + } + static hasStaticField(x: object) { + return #staticField in x; + ~~~~~~~~~~~~ +!!! error TS2343: This syntax requires an imported helper named '__classPrivateFieldIn' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. + } + } + +==== node_modules/tslib/index.d.ts (0 errors) ==== + // Provides only the decorator helpers, deliberately omitting __classPrivateField{Get,Set,In}, to + // confirm that accessing a decorated class's static private elements still requires them. + export declare function __esDecorate(ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any): void; + export declare function __runInitializers(thisArg: any, initializers: any[], value?: any): any; + export declare function __setFunctionName(f: any, name: any, prefix?: string): any; + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.js b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.js new file mode 100644 index 00000000000..06eb8b5520f --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.js @@ -0,0 +1,103 @@ +//// [tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.ts] //// + +//// [main.ts] +export declare var dec: any; + +// Class decorators hoist a class's static private/auto-accessor elements out of the class body, so +// accessing them still requires the classPrivateField* helpers even at targets (like es2022) that +// otherwise support private fields natively. Instance private fields are unaffected by this and stay +// fully native even in a decorated class. +@dec +export class Foo { + #instanceField = 1; + static #staticField = 1; + static #staticMethod() { return 1; } + static get #staticAccessor() { return 1; } + static set #staticAccessor(v: number) {} + static accessor #staticAutoAccessor = 1; + + getInstanceField() { + return this.#instanceField; + } + static getStaticField() { + return Foo.#staticField; + } + static callStaticMethod() { + return Foo.#staticMethod(); + } + static useStaticAccessor() { + Foo.#staticAccessor = Foo.#staticAccessor; + } + static useStaticAutoAccessor() { + Foo.#staticAutoAccessor = Foo.#staticAutoAccessor; + } + static hasStaticField(x: object) { + return #staticField in x; + } +} + +//// [index.d.ts] +// Provides only the decorator helpers, deliberately omitting __classPrivateField{Get,Set,In}, to +// confirm that accessing a decorated class's static private elements still requires them. +export declare function __esDecorate(ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any): void; +export declare function __runInitializers(thisArg: any, initializers: any[], value?: any): any; +export declare function __setFunctionName(f: any, name: any, prefix?: string): any; + + +//// [main.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Foo = void 0; +const tslib_1 = require("tslib"); +// Class decorators hoist a class's static private/auto-accessor elements out of the class body, so +// accessing them still requires the classPrivateField* helpers even at targets (like es2022) that +// otherwise support private fields natively. Instance private fields are unaffected by this and stay +// fully native even in a decorated class. +let Foo = (() => { + var _Foo_staticField, _Foo_staticMethod, _Foo_staticAccessor_get, _Foo_staticAccessor_set, _Foo_staticAutoAccessor_get, _Foo_staticAutoAccessor_set, _Foo_staticAutoAccessor_accessor_storage; + let _classDecorators = [exports.dec]; + let _classDescriptor; + let _classExtraInitializers = []; + let _classThis; + var Foo = class { + static { _classThis = this; } + static { tslib_1.__setFunctionName(this, "Foo"); } + static { _Foo_staticMethod = function _Foo_staticMethod() { return 1; }, _Foo_staticAccessor_get = function _Foo_staticAccessor_get() { return 1; }, _Foo_staticAccessor_set = function _Foo_staticAccessor_set(v) { }, _Foo_staticAutoAccessor_get = function _Foo_staticAutoAccessor_get() { return tslib_1.__classPrivateFieldGet(_classThis, _classThis, "f", _Foo_staticAutoAccessor_accessor_storage); }, _Foo_staticAutoAccessor_set = function _Foo_staticAutoAccessor_set(value) { tslib_1.__classPrivateFieldSet(_classThis, _classThis, value, "f", _Foo_staticAutoAccessor_accessor_storage); }; } + static { + const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; + tslib_1.__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); + Foo = _classThis = _classDescriptor.value; + if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); + } + #instanceField = 1; + static { + _Foo_staticField = { value: 1 }; + } + static { + _Foo_staticAutoAccessor_accessor_storage = { value: 1 }; + } + getInstanceField() { + return this.#instanceField; + } + static getStaticField() { + return tslib_1.__classPrivateFieldGet(Foo, _classThis, "f", _Foo_staticField); + } + static callStaticMethod() { + return tslib_1.__classPrivateFieldGet(Foo, _classThis, "m", _Foo_staticMethod).call(Foo); + } + static useStaticAccessor() { + tslib_1.__classPrivateFieldSet(Foo, _classThis, tslib_1.__classPrivateFieldGet(Foo, _classThis, "a", _Foo_staticAccessor_get), "a", _Foo_staticAccessor_set); + } + static useStaticAutoAccessor() { + tslib_1.__classPrivateFieldSet(Foo, _classThis, tslib_1.__classPrivateFieldGet(Foo, _classThis, "a", _Foo_staticAutoAccessor_get), "a", _Foo_staticAutoAccessor_set); + } + static hasStaticField(x) { + return tslib_1.__classPrivateFieldIn(_classThis, x); + } + static { + tslib_1.__runInitializers(_classThis, _classExtraInitializers); + } + }; + return Foo = _classThis; +})(); +exports.Foo = Foo; diff --git a/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.symbols b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.symbols new file mode 100644 index 00000000000..45a2a7150a9 --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.symbols @@ -0,0 +1,108 @@ +//// [tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.ts] //// + +=== main.ts === +export declare var dec: any; +>dec : Symbol(dec, Decl(main.ts, 0, 18)) + +// Class decorators hoist a class's static private/auto-accessor elements out of the class body, so +// accessing them still requires the classPrivateField* helpers even at targets (like es2022) that +// otherwise support private fields natively. Instance private fields are unaffected by this and stay +// fully native even in a decorated class. +@dec +>dec : Symbol(dec, Decl(main.ts, 0, 18)) + +export class Foo { +>Foo : Symbol(Foo, Decl(main.ts, 0, 28)) + + #instanceField = 1; +>#instanceField : Symbol(Foo.#instanceField, Decl(main.ts, 7, 18)) + + static #staticField = 1; +>#staticField : Symbol(Foo.#staticField, Decl(main.ts, 8, 23)) + + static #staticMethod() { return 1; } +>#staticMethod : Symbol(Foo.#staticMethod, Decl(main.ts, 9, 28)) + + static get #staticAccessor() { return 1; } +>#staticAccessor : Symbol(Foo.#staticAccessor, Decl(main.ts, 10, 40), Decl(main.ts, 11, 46)) + + static set #staticAccessor(v: number) {} +>#staticAccessor : Symbol(Foo.#staticAccessor, Decl(main.ts, 10, 40), Decl(main.ts, 11, 46)) +>v : Symbol(v, Decl(main.ts, 12, 31)) + + static accessor #staticAutoAccessor = 1; +>#staticAutoAccessor : Symbol(Foo.#staticAutoAccessor, Decl(main.ts, 12, 44)) + + getInstanceField() { +>getInstanceField : Symbol(Foo.getInstanceField, Decl(main.ts, 13, 44)) + + return this.#instanceField; +>this.#instanceField : Symbol(Foo.#instanceField, Decl(main.ts, 7, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 28)) + } + static getStaticField() { +>getStaticField : Symbol(Foo.getStaticField, Decl(main.ts, 17, 5)) + + return Foo.#staticField; +>Foo.#staticField : Symbol(Foo.#staticField, Decl(main.ts, 8, 23)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 28)) + } + static callStaticMethod() { +>callStaticMethod : Symbol(Foo.callStaticMethod, Decl(main.ts, 20, 5)) + + return Foo.#staticMethod(); +>Foo.#staticMethod : Symbol(Foo.#staticMethod, Decl(main.ts, 9, 28)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 28)) + } + static useStaticAccessor() { +>useStaticAccessor : Symbol(Foo.useStaticAccessor, Decl(main.ts, 23, 5)) + + Foo.#staticAccessor = Foo.#staticAccessor; +>Foo.#staticAccessor : Symbol(Foo.#staticAccessor, Decl(main.ts, 10, 40), Decl(main.ts, 11, 46)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 28)) +>Foo.#staticAccessor : Symbol(Foo.#staticAccessor, Decl(main.ts, 10, 40), Decl(main.ts, 11, 46)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 28)) + } + static useStaticAutoAccessor() { +>useStaticAutoAccessor : Symbol(Foo.useStaticAutoAccessor, Decl(main.ts, 26, 5)) + + Foo.#staticAutoAccessor = Foo.#staticAutoAccessor; +>Foo.#staticAutoAccessor : Symbol(Foo.#staticAutoAccessor, Decl(main.ts, 12, 44)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 28)) +>Foo.#staticAutoAccessor : Symbol(Foo.#staticAutoAccessor, Decl(main.ts, 12, 44)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 28)) + } + static hasStaticField(x: object) { +>hasStaticField : Symbol(Foo.hasStaticField, Decl(main.ts, 29, 5)) +>x : Symbol(x, Decl(main.ts, 30, 26)) + + return #staticField in x; +>#staticField : Symbol(Foo.#staticField, Decl(main.ts, 8, 23)) +>x : Symbol(x, Decl(main.ts, 30, 26)) + } +} + +=== node_modules/tslib/index.d.ts === +// Provides only the decorator helpers, deliberately omitting __classPrivateField{Get,Set,In}, to +// confirm that accessing a decorated class's static private elements still requires them. +export declare function __esDecorate(ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any): void; +>__esDecorate : Symbol(__esDecorate, Decl(index.d.ts, 0, 0)) +>ctor : Symbol(ctor, Decl(index.d.ts, 2, 37)) +>descriptorIn : Symbol(descriptorIn, Decl(index.d.ts, 2, 47)) +>decorators : Symbol(decorators, Decl(index.d.ts, 2, 66)) +>contextIn : Symbol(contextIn, Decl(index.d.ts, 2, 85)) +>initializers : Symbol(initializers, Decl(index.d.ts, 2, 101)) +>extraInitializers : Symbol(extraInitializers, Decl(index.d.ts, 2, 120)) + +export declare function __runInitializers(thisArg: any, initializers: any[], value?: any): any; +>__runInitializers : Symbol(__runInitializers, Decl(index.d.ts, 2, 151)) +>thisArg : Symbol(thisArg, Decl(index.d.ts, 3, 42)) +>initializers : Symbol(initializers, Decl(index.d.ts, 3, 55)) +>value : Symbol(value, Decl(index.d.ts, 3, 76)) + +export declare function __setFunctionName(f: any, name: any, prefix?: string): any; +>__setFunctionName : Symbol(__setFunctionName, Decl(index.d.ts, 3, 95)) +>f : Symbol(f, Decl(index.d.ts, 4, 42)) +>name : Symbol(name, Decl(index.d.ts, 4, 49)) +>prefix : Symbol(prefix, Decl(index.d.ts, 4, 60)) + diff --git a/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.types b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.types new file mode 100644 index 00000000000..c148d2bd5ca --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.types @@ -0,0 +1,117 @@ +//// [tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.ts] //// + +=== main.ts === +export declare var dec: any; +>dec : any + +// Class decorators hoist a class's static private/auto-accessor elements out of the class body, so +// accessing them still requires the classPrivateField* helpers even at targets (like es2022) that +// otherwise support private fields natively. Instance private fields are unaffected by this and stay +// fully native even in a decorated class. +@dec +>dec : any + +export class Foo { +>Foo : Foo + + #instanceField = 1; +>#instanceField : number +>1 : 1 + + static #staticField = 1; +>#staticField : number +>1 : 1 + + static #staticMethod() { return 1; } +>#staticMethod : () => number +>1 : 1 + + static get #staticAccessor() { return 1; } +>#staticAccessor : number +>1 : 1 + + static set #staticAccessor(v: number) {} +>#staticAccessor : number +>v : number + + static accessor #staticAutoAccessor = 1; +>#staticAutoAccessor : number +>1 : 1 + + getInstanceField() { +>getInstanceField : () => number + + return this.#instanceField; +>this.#instanceField : number +>this : this + } + static getStaticField() { +>getStaticField : () => number + + return Foo.#staticField; +>Foo.#staticField : number +>Foo : typeof Foo + } + static callStaticMethod() { +>callStaticMethod : () => number + + return Foo.#staticMethod(); +>Foo.#staticMethod() : number +>Foo.#staticMethod : () => number +>Foo : typeof Foo + } + static useStaticAccessor() { +>useStaticAccessor : () => void + + Foo.#staticAccessor = Foo.#staticAccessor; +>Foo.#staticAccessor = Foo.#staticAccessor : number +>Foo.#staticAccessor : number +>Foo : typeof Foo +>Foo.#staticAccessor : number +>Foo : typeof Foo + } + static useStaticAutoAccessor() { +>useStaticAutoAccessor : () => void + + Foo.#staticAutoAccessor = Foo.#staticAutoAccessor; +>Foo.#staticAutoAccessor = Foo.#staticAutoAccessor : number +>Foo.#staticAutoAccessor : number +>Foo : typeof Foo +>Foo.#staticAutoAccessor : number +>Foo : typeof Foo + } + static hasStaticField(x: object) { +>hasStaticField : (x: object) => x is typeof Foo +>x : object + + return #staticField in x; +>#staticField in x : boolean +>#staticField : any +>x : object + } +} + +=== node_modules/tslib/index.d.ts === +// Provides only the decorator helpers, deliberately omitting __classPrivateField{Get,Set,In}, to +// confirm that accessing a decorated class's static private elements still requires them. +export declare function __esDecorate(ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any): void; +>__esDecorate : (ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any) => void +>ctor : any +>descriptorIn : any +>decorators : any[] +>contextIn : any +>initializers : any +>extraInitializers : any + +export declare function __runInitializers(thisArg: any, initializers: any[], value?: any): any; +>__runInitializers : (thisArg: any, initializers: any[], value?: any) => any +>thisArg : any +>initializers : any[] +>value : any + +export declare function __setFunctionName(f: any, name: any, prefix?: string): any; +>__setFunctionName : (f: any, name: any, prefix?: string) => any +>f : any +>name : any +>prefix : string | undefined + diff --git a/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.errors.txt b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.errors.txt new file mode 100644 index 00000000000..876cf138b79 --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.errors.txt @@ -0,0 +1,32 @@ +main.ts(16,16): error TS2343: This syntax requires an imported helper named '__classPrivateFieldGet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. + + +==== main.ts (1 errors) ==== + export declare var dec: any; + + // At useDefineForClassFields: false, native decorators are still lowered even at esnext (this is the + // one case where the esnext decorator transform isn't skipped by target alone - see + // newESDecoratorTransformer's skip condition), so a decorated class's static private elements still + // need the classPrivateField* helpers here too. + @dec + export class Foo { + #instanceField = 1; + static #staticField = 1; + + getInstanceField() { + return this.#instanceField; + } + static getStaticField() { + return Foo.#staticField; + ~~~~~~~~~~~~~~~~ +!!! error TS2343: This syntax requires an imported helper named '__classPrivateFieldGet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. + } + } + +==== node_modules/tslib/index.d.ts (0 errors) ==== + // Provides only the decorator helpers, deliberately omitting __classPrivateFieldGet, to confirm that + // accessing the decorated class's static private field still requires it. + export declare function __esDecorate(ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any): void; + export declare function __runInitializers(thisArg: any, initializers: any[], value?: any): any; + export declare function __setFunctionName(f: any, name: any, prefix?: string): any; + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.js b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.js new file mode 100644 index 00000000000..bc2c3ec7a2f --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.js @@ -0,0 +1,71 @@ +//// [tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.ts] //// + +//// [main.ts] +export declare var dec: any; + +// At useDefineForClassFields: false, native decorators are still lowered even at esnext (this is the +// one case where the esnext decorator transform isn't skipped by target alone - see +// newESDecoratorTransformer's skip condition), so a decorated class's static private elements still +// need the classPrivateField* helpers here too. +@dec +export class Foo { + #instanceField = 1; + static #staticField = 1; + + getInstanceField() { + return this.#instanceField; + } + static getStaticField() { + return Foo.#staticField; + } +} + +//// [index.d.ts] +// Provides only the decorator helpers, deliberately omitting __classPrivateFieldGet, to confirm that +// accessing the decorated class's static private field still requires it. +export declare function __esDecorate(ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any): void; +export declare function __runInitializers(thisArg: any, initializers: any[], value?: any): any; +export declare function __setFunctionName(f: any, name: any, prefix?: string): any; + + +//// [main.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Foo = void 0; +const tslib_1 = require("tslib"); +// At useDefineForClassFields: false, native decorators are still lowered even at esnext (this is the +// one case where the esnext decorator transform isn't skipped by target alone - see +// newESDecoratorTransformer's skip condition), so a decorated class's static private elements still +// need the classPrivateField* helpers here too. +let Foo = (() => { + var _Foo_staticField; + let _classDecorators = [exports.dec]; + let _classDescriptor; + let _classExtraInitializers = []; + let _classThis; + var Foo = class { + static { _classThis = this; } + static { tslib_1.__setFunctionName(this, "Foo"); } + static { + const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; + tslib_1.__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); + Foo = _classThis = _classDescriptor.value; + if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); + } + #instanceField = 1; + static { + _Foo_staticField = { value: 1 }; + } + getInstanceField() { + return this.#instanceField; + } + static getStaticField() { + return tslib_1.__classPrivateFieldGet(Foo, _classThis, "f", _Foo_staticField); + } + static { + tslib_1.__runInitializers(_classThis, _classExtraInitializers); + } + }; + return Foo = _classThis; +})(); +exports.Foo = Foo; diff --git a/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.symbols b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.symbols new file mode 100644 index 00000000000..630451a0f4f --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.symbols @@ -0,0 +1,62 @@ +//// [tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.ts] //// + +=== main.ts === +export declare var dec: any; +>dec : Symbol(dec, Decl(main.ts, 0, 18)) + +// At useDefineForClassFields: false, native decorators are still lowered even at esnext (this is the +// one case where the esnext decorator transform isn't skipped by target alone - see +// newESDecoratorTransformer's skip condition), so a decorated class's static private elements still +// need the classPrivateField* helpers here too. +@dec +>dec : Symbol(dec, Decl(main.ts, 0, 18)) + +export class Foo { +>Foo : Symbol(Foo, Decl(main.ts, 0, 28)) + + #instanceField = 1; +>#instanceField : Symbol(Foo.#instanceField, Decl(main.ts, 7, 18)) + + static #staticField = 1; +>#staticField : Symbol(Foo.#staticField, Decl(main.ts, 8, 23)) + + getInstanceField() { +>getInstanceField : Symbol(Foo.getInstanceField, Decl(main.ts, 9, 28)) + + return this.#instanceField; +>this.#instanceField : Symbol(Foo.#instanceField, Decl(main.ts, 7, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 28)) + } + static getStaticField() { +>getStaticField : Symbol(Foo.getStaticField, Decl(main.ts, 13, 5)) + + return Foo.#staticField; +>Foo.#staticField : Symbol(Foo.#staticField, Decl(main.ts, 8, 23)) +>Foo : Symbol(Foo, Decl(main.ts, 0, 28)) + } +} + +=== node_modules/tslib/index.d.ts === +// Provides only the decorator helpers, deliberately omitting __classPrivateFieldGet, to confirm that +// accessing the decorated class's static private field still requires it. +export declare function __esDecorate(ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any): void; +>__esDecorate : Symbol(__esDecorate, Decl(index.d.ts, 0, 0)) +>ctor : Symbol(ctor, Decl(index.d.ts, 2, 37)) +>descriptorIn : Symbol(descriptorIn, Decl(index.d.ts, 2, 47)) +>decorators : Symbol(decorators, Decl(index.d.ts, 2, 66)) +>contextIn : Symbol(contextIn, Decl(index.d.ts, 2, 85)) +>initializers : Symbol(initializers, Decl(index.d.ts, 2, 101)) +>extraInitializers : Symbol(extraInitializers, Decl(index.d.ts, 2, 120)) + +export declare function __runInitializers(thisArg: any, initializers: any[], value?: any): any; +>__runInitializers : Symbol(__runInitializers, Decl(index.d.ts, 2, 151)) +>thisArg : Symbol(thisArg, Decl(index.d.ts, 3, 42)) +>initializers : Symbol(initializers, Decl(index.d.ts, 3, 55)) +>value : Symbol(value, Decl(index.d.ts, 3, 76)) + +export declare function __setFunctionName(f: any, name: any, prefix?: string): any; +>__setFunctionName : Symbol(__setFunctionName, Decl(index.d.ts, 3, 95)) +>f : Symbol(f, Decl(index.d.ts, 4, 42)) +>name : Symbol(name, Decl(index.d.ts, 4, 49)) +>prefix : Symbol(prefix, Decl(index.d.ts, 4, 60)) + diff --git a/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.types b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.types new file mode 100644 index 00000000000..a9e6984f1bf --- /dev/null +++ b/testdata/baselines/reference/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.types @@ -0,0 +1,64 @@ +//// [tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.ts] //// + +=== main.ts === +export declare var dec: any; +>dec : any + +// At useDefineForClassFields: false, native decorators are still lowered even at esnext (this is the +// one case where the esnext decorator transform isn't skipped by target alone - see +// newESDecoratorTransformer's skip condition), so a decorated class's static private elements still +// need the classPrivateField* helpers here too. +@dec +>dec : any + +export class Foo { +>Foo : Foo + + #instanceField = 1; +>#instanceField : number +>1 : 1 + + static #staticField = 1; +>#staticField : number +>1 : 1 + + getInstanceField() { +>getInstanceField : () => number + + return this.#instanceField; +>this.#instanceField : number +>this : this + } + static getStaticField() { +>getStaticField : () => number + + return Foo.#staticField; +>Foo.#staticField : number +>Foo : typeof Foo + } +} + +=== node_modules/tslib/index.d.ts === +// Provides only the decorator helpers, deliberately omitting __classPrivateFieldGet, to confirm that +// accessing the decorated class's static private field still requires it. +export declare function __esDecorate(ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any): void; +>__esDecorate : (ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any) => void +>ctor : any +>descriptorIn : any +>decorators : any[] +>contextIn : any +>initializers : any +>extraInitializers : any + +export declare function __runInitializers(thisArg: any, initializers: any[], value?: any): any; +>__runInitializers : (thisArg: any, initializers: any[], value?: any) => any +>thisArg : any +>initializers : any[] +>value : any + +export declare function __setFunctionName(f: any, name: any, prefix?: string): any; +>__setFunctionName : (f: any, name: any, prefix?: string) => any +>f : any +>name : any +>prefix : string | undefined + diff --git a/testdata/tests/cases/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.ts b/testdata/tests/cases/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.ts new file mode 100644 index 00000000000..2f4ef532634 --- /dev/null +++ b/testdata/tests/cases/compiler/importHelpersNotRequiredForDecoratedStaticPrivateElementsAtESNext.ts @@ -0,0 +1,16 @@ +// @importHelpers: true +// @target: esnext +// @module: commonjs +// @filename: main.ts +export declare var dec: any; + +// At target: esnext with the default useDefineForClassFields: true, native decorators need no +// transform at all (see newESDecoratorTransformer's skip condition), so even a decorated class's +// static private elements stay fully native and never need the classPrivateField* helpers. +@dec +export class Foo { + static #staticField = 1; + static getStaticField() { + return Foo.#staticField; + } +} diff --git a/testdata/tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.ts b/testdata/tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.ts new file mode 100644 index 00000000000..aecb8abd737 --- /dev/null +++ b/testdata/tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElements.ts @@ -0,0 +1,45 @@ +// @importHelpers: true +// @target: es2022 +// @module: commonjs +// @filename: main.ts +export declare var dec: any; + +// Class decorators hoist a class's static private/auto-accessor elements out of the class body, so +// accessing them still requires the classPrivateField* helpers even at targets (like es2022) that +// otherwise support private fields natively. Instance private fields are unaffected by this and stay +// fully native even in a decorated class. +@dec +export class Foo { + #instanceField = 1; + static #staticField = 1; + static #staticMethod() { return 1; } + static get #staticAccessor() { return 1; } + static set #staticAccessor(v: number) {} + static accessor #staticAutoAccessor = 1; + + getInstanceField() { + return this.#instanceField; + } + static getStaticField() { + return Foo.#staticField; + } + static callStaticMethod() { + return Foo.#staticMethod(); + } + static useStaticAccessor() { + Foo.#staticAccessor = Foo.#staticAccessor; + } + static useStaticAutoAccessor() { + Foo.#staticAutoAccessor = Foo.#staticAutoAccessor; + } + static hasStaticField(x: object) { + return #staticField in x; + } +} + +// @filename: node_modules/tslib/index.d.ts +// Provides only the decorator helpers, deliberately omitting __classPrivateField{Get,Set,In}, to +// confirm that accessing a decorated class's static private elements still requires them. +export declare function __esDecorate(ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any): void; +export declare function __runInitializers(thisArg: any, initializers: any[], value?: any): any; +export declare function __setFunctionName(f: any, name: any, prefix?: string): any; diff --git a/testdata/tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.ts b/testdata/tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.ts new file mode 100644 index 00000000000..d7fb3003e50 --- /dev/null +++ b/testdata/tests/cases/compiler/importHelpersRequiredForDecoratedStaticPrivateElementsUseDefineForClassFieldsFalse.ts @@ -0,0 +1,30 @@ +// @importHelpers: true +// @target: esnext +// @useDefineForClassFields: false +// @module: commonjs +// @filename: main.ts +export declare var dec: any; + +// At useDefineForClassFields: false, native decorators are still lowered even at esnext (this is the +// one case where the esnext decorator transform isn't skipped by target alone - see +// newESDecoratorTransformer's skip condition), so a decorated class's static private elements still +// need the classPrivateField* helpers here too. +@dec +export class Foo { + #instanceField = 1; + static #staticField = 1; + + getInstanceField() { + return this.#instanceField; + } + static getStaticField() { + return Foo.#staticField; + } +} + +// @filename: node_modules/tslib/index.d.ts +// Provides only the decorator helpers, deliberately omitting __classPrivateFieldGet, to confirm that +// accessing the decorated class's static private field still requires it. +export declare function __esDecorate(ctor: any, descriptorIn: any, decorators: any[], contextIn: any, initializers: any, extraInitializers: any): void; +export declare function __runInitializers(thisArg: any, initializers: any[], value?: any): any; +export declare function __setFunctionName(f: any, name: any, prefix?: string): any;