`--experimentalDecorators`: decorated `abstract` property is emitted as a class field (regression in 7.0)

未關閉
#64,980 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

維護者通常 1 天內回覆

@1-DARK 已經在處理了。

開始於 2026年10月27日。

  • #64982 來自 @1-DARK —— 未關閉

評估

難度
4/5
預估耗時
1-2 天
新手友好度
35/100
Issue 類型
缺陷
描述清晰度
描述清楚
活躍度
停滯
技術堆疊
typescript
領域
compilers

研究方向

Start by reading typeeraser.go around the decorated declare and abstract property handling, then follow visitPropertyDeclaration in legacydecorators.go to see why the preserved node emits a field. Compare with the visitPropertyDeclaration handling in ts.ts described in the issue. Done means the abstract property is erased while its legacy decorator call remains, matching the expected output; an open pull request is already linked.

由索引模型根據 Issue 內容生成。

描述

Bug
🔎 Search Terms

experimentalDecorators, abstract property, decorator, class field, useDefineForClassFields, typeeraser, legacy decorators

🕗 Version & Regression Information
  • Regression from tsc 6.0.3, which erases the field.
  • Reproduces in 7.0.2, 7.1.0-beta and 7.1.0-dev.20261026.1.
💻 Code
function dec(target: object, key: string) {
  Object.defineProperty(target, key, { get: () => 'from prototype', configurable: true });
}

abstract class Base {
  @dec abstract data: string;
}

class Sub extends Base {
  declare data: string;
}

const s = new Sub();
console.log(Object.hasOwn(s, 'data'), s.data);
{ "compilerOptions": { "target": "es2022", "experimentalDecorators": true } }
🙁 Actual behavior

Base gets a data; field, so every instance has an own data: undefined that shadows whatever the decorator put on the prototype. Prints true undefined.

class Base {
    data;
}
__decorate([
    dec
], Base.prototype, "data", void 0);
🙂 Expected behavior

Same as tsc 6.0.3: the field is erased and only the __decorate call remains. Prints false from prototype.

class Base {
}
__decorate([
    dec
], Base.prototype, "data", void 0);
Additional information about the issue

The field appears only when all three hold: experimentalDecorators, a decorator on the abstract property, and define semantics (useDefineForClassFields, the default at target ES2022 and later). emitDecoratorMetadata makes no difference. An undecorated abstract property and a decorated declare property are both still erased.

The likely cause is in typeeraser.go. For a decorated declare/abstract property under experimentalDecorators, it keeps the node so that LegacyDecoratorsTransformer can emit the __decorate call. But it rebuilds the node with VisitModifiers, which elides AbstractKeyword. visitPropertyDeclaration in legacydecorators.go then finds neither NodeFlagsAmbient nor ModifierFlagsAbstract and emits a field. declare escapes this because the node keeps NodeFlagsAmbient. Strada's visitPropertyDeclaration in ts.ts handles the same case by appending a declare modifier to the preserved node (concatenate(modifiers, factory.createModifiersFromModifierFlags(ModifierFlags.Ambient))).

We hit this in TypeORM entities: an abstract base declares a decorated abstract column, each subclass redeclares it with declare, and every entity instance gains an own data: undefined it doesn't have under tsc 6.

主要語言
Go
星號
112k
分支
15.4k
平均合併
3 天 1 小時
30 天內合併 PR
136

環境準備

在 Codespaces 中開啟

在瀏覽器裡用你自己的 GitHub 帳號啟動這個專案的開發容器。

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

microsoft/TypeScript 的其他 Issue

查看 microsoft/TypeScript 的全部 Issue

相似的 Issue

更多 Go Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。