Skip to content

fix(transformer/class-properties): use correct property name when converting parameter properties#21268

Merged
Dunqing merged 1 commit intooxc-project:mainfrom
AmalJossy:fix/typescript-param-property-clash-rename
Apr 13, 2026
Merged

fix(transformer/class-properties): use correct property name when converting parameter properties#21268
Dunqing merged 1 commit intooxc-project:mainfrom
AmalJossy:fix/typescript-param-property-clash-rename

Conversation

@AmalJossy
Copy link
Copy Markdown
Contributor

Summary

Fixes #21116 where TypeScript constructor parameter properties emit the wrong property name when the parameter name clashes with a free variable referenced in a class field initializer.

Problem

When targeting pre-ES2022 (or with transform-class-properties enabled), the class properties plugin detects that a constructor parameter name shadows an outer variable referenced in a field initializer, and renames the parameter binding (e.g. x_x) to preserve the initializer's semantics.

convert_constructor_params (TypeScript plugin) ran after this rename and read id.name — which had already been mutated — to produce the this.X = X assignment. This caused:

let x = 10;
class Foo {
  field = x;
  constructor(public x: number) {}
}

to incorrectly emit:

constructor(_x) {
  this._x = _x; // ❌ property name should be x
  ...
}

Root Cause

Both plugins share a single traversal pass with interleaved hooks. enter_class_body (class properties) fires before enter_method_definition (TypeScript), so by the time convert_constructor_params runs, BindingIdentifier.name has already been mutated by ConstructorSymbolRenamer.

Fix

Recover the property name from id.span.source_text(source_text) instead of id.name. Spans are set by the parser and never mutated by any transformer, so this always returns the original user-written identifier regardless of renames.

constructor(_x) {
  this.x = _x; // ✅
  ...
}

AI Disclosure

This fix was developed with Claude Code assistance.

@github-actions github-actions Bot added A-transformer Area - Transformer / Transpiler C-bug Category - Bug labels Apr 9, 2026
@Dunqing Dunqing force-pushed the fix/typescript-param-property-clash-rename branch from 548dd42 to 1a8864f Compare April 10, 2026 06:39
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 11, 2026

Merging this PR will not alter performance

✅ 44 untouched benchmarks
⏩ 7 skipped benchmarks1


Comparing AmalJossy:fix/typescript-param-property-clash-rename (0cfe768) with main (6dd061c)

Open in CodSpeed

Footnotes

  1. 7 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

…m property name when clashing symbol is renamed

When class-properties transform renames a constructor parameter to avoid
shadowing (e.g. `x` -> `_x`), the BindingIdentifier.name is mutated before
the TypeScript param property transform reads it. This caused `this._x = _x`
instead of the correct `this.x = _x`.

Use `span.source_text()` to recover the original parameter name from source,
which is unaffected by the rename pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Dunqing Dunqing force-pushed the fix/typescript-param-property-clash-rename branch from 3229ffd to 0cfe768 Compare April 11, 2026 15:23
@Dunqing Dunqing changed the title fix(transformer): use correct property name when converting parameter properties fix(transformer/class-properties): use correct property name when converting parameter properties Apr 13, 2026
Copy link
Copy Markdown
Member

@Dunqing Dunqing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@Dunqing Dunqing merged commit af1a586 into oxc-project:main Apr 13, 2026
34 checks passed
camc314 pushed a commit that referenced this pull request Apr 13, 2026
### 💥 BREAKING CHANGES

- 36cdc31 str: [**BREAKING**] Remove identity `FromIn` impl for `Ident`
(#21251) (overlookmotel)
- 382958a span: [**BREAKING**] Remove re-exports of string types from
`oxc_span` crate (#21246) (overlookmotel)
- c4aedfa str: [**BREAKING**] Add `static_ident!` macro (#21245)
(overlookmotel)

### 🚀 Features

- e7e1aea transformer/typescript: Add `optimize_enums` option for
regular enum inlining (#20539) (Dunqing)
- 679f57f transformer/typescript: Implement const enum inlining and
declaration removal (#20508) (Dunqing)
- 6dd061c semantic: Extend `MemberWriteTarget` to cover all property
modification patterns (#21205) (Dunqing)
- f134e24 minifier: Support `property_write_side_effects` option to drop
unused property assignments (#20773) (Dunqing)
- 75663c0 semantic: Add enum member value evaluation for const enum
support (#20602) (Dunqing)
- 3cfe8ed semantic: Add `MemberWriteTarget` flag to `ReferenceFlags`
(#20772) (Dunqing)

### 🐛 Bug Fixes

- af1a586 transformer/class-properties: Use correct property name when
converting parameter properties (#21268) (Amal Jossy)
- b43250a allocator: Move allocation tracking into `Bump` (#21342)
(overlookmotel)
- 36f505f allocator: `StringBuilder` use `Allocator::alloc_layout`
(#21340) (overlookmotel)
- 7a08a6f allocator: Fix allocation counting in
`Allocator::alloc_concat_strs_array` (#21336) (overlookmotel)
- 2338e28 ecmascript: Treat `this` as potentially having side effects
(#21297) (sapphi-red)
- bd8bd39 allocator: Remove unsafe hacks from `from_raw_parts` methods
(#21283) (overlookmotel)
- 8f4c340 allocator: Remove dangerous pointer const to mut cast (#21279)
(overlookmotel)
- aa9259f parser: Add missing error code for optional param diagnostic
(#21258) (camc314)
- 04b3c2f str: Fix unsound casting const pointers to mut pointers
(#21242) (overlookmotel)
- ceadf6c str: Make `Ident::from_raw` an unsafe function (#21241)
(overlookmotel)
- eab13b3 transformer/decorators: Avoid accessor storage name collisions
(#21106) (Dunqing)
- 07e8a30 transformer/react-refresh: Handle parenthesized variable
initializers (#21047) (camc314)

### ⚡ Performance

- c3ca6f6 allocator: `StringBuilder::from_strs_array_in` check for 0
length earlier (#21338) (overlookmotel)
- c2422bb allocator: `Allocator::alloc_concat_strs_array` check for 0
length earlier (#21337) (overlookmotel)
- 04b0fdc allocator: Mark `Allocator::alloc_layout` as
`#[inline(always)]` (#21335) (overlookmotel)
- 17aee9e allocator: Use `offset_from_unsigned` in
`ChunkFooter::as_raw_parts` (#21280) (overlookmotel)
- 61adedd minifier: Fix O(n²) perf on very many var decls (#21062)
(Gunnlaugur Thor Briem)
- addcd02 napi/parser, linter/plugins: Raw transfer deserializer for
`Vec`s use shift instead of multiply where possible (#21142)
(overlookmotel)
- 3068ded napi/parser, linter/plugins: Shift before add when calculating
positions in raw transfer deserializer (#21141) (overlookmotel)
- eb400b8 napi/parser, linter/plugins: Remove `uint32` buffer view
(#21140) (overlookmotel)
- 2675085 napi/parser: Lazy deserialization use only `Int32Array`
(#21139) (overlookmotel)
- 5b35a53 napi/parser: Deserializing tokens use only `int32` array
(#21138) (overlookmotel)
- f163d10 parser: Tokens raw deserialization use `Int32Array` (#21137)
(overlookmotel)
- 7a86613 linter/plugins: Use `Int32Array`s for tokens and comments
buffers (#21136) (overlookmotel)
- 8c51121 napi/parser, linter/plugins: Raw transfer deserialize `Span`
fields as `i32`s (#21135) (overlookmotel)
- bc1bcdd napi/parser, linter/plugins: Inline trivial raw transfer field
deserializers into node object definitions (#21134) (overlookmotel)
- c0278ab napi/parser, linter/plugins: Use `Int32Array` in raw transfer
deserializer (#21132) (overlookmotel)
- 43482c7 linter/plugins: Use `>>` not `>>>` in binary search loops
(#21129) (overlookmotel)

### 📚 Documentation

- f5e1845 allocator: Upgrade headers in doc comments for `Bump` (#21263)
(overlookmotel)
- 2870174 allocator: Upper case `SAFETY` in comments (#21253)
(overlookmotel)
- 01bc269 str: Reformat `Ident` doc comments (#21240) (overlookmotel)
- dd47359 allocator: Add doc comments for panics and errors (#21230)
(overlookmotel)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-transformer Area - Transformer / Transpiler C-bug Category - Bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect renaming of constructor parameter property causes broken this reference in target es2016

2 participants