Skip to content

transformer: legacy decorator on computed property key: variable declared but not assigned #20418

@stryaponoff

Description

@stryaponoff

Summary

When using legacy decorators with emitDecoratorMetadata: false on a class property that has a computed property key (e.g. [MY_CONST]), the oxc transformer emits var _MY_CONST; but never assigns _MY_CONST = MY_CONST. This leaves the key as undefined at runtime, causing a TypeError inside Reflect.decorate.

Reproduction

Input TypeScript:

import { FIELD_NAME } from './constants'

function Field() {
  return (target: any, key: string) => {}
}

class MyModel {
  @Field()
  public [FIELD_NAME]: string
}

where constants.ts contains:

export const FIELD_NAME = 'myField'

tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": false
  }
}

Actual output (oxc):

import { FIELD_NAME } from "./constants";
import _decorate from "@oxc-project/runtime/helpers/decorate";
var _FIELD_NAME;  // ← declared but never assigned
class MyModel {}
_decorate([Field()], MyModel.prototype, _FIELD_NAME, void 0);
//                                      ^^^^^^^^^^^ undefined

Expected output (matching TypeScript's tsc):

var _a;
_a = FIELD_NAME;
// ...
__decorate([Field()], MyModel.prototype, _a, void 0);

Or equivalently:

var _FIELD_NAME = FIELD_NAME;
// ...
_decorate([Field()], MyModel.prototype, _FIELD_NAME, void 0);

Impact

When reflect-metadata is loaded, __decorate delegates to Reflect.decorate(). Since key is undefined, Reflect.decorate interprets this as a class decorator call and throws TypeError because target (a prototype object) is not a constructor.

Even without reflect-metadata, the decorator receives undefined instead of the actual property name, silently breaking any decorator that relies on the key argument.

Versions

  • @oxc-project/runtime: 0.115.0
  • rolldown: 1.0.0-rc.9
  • vite: 8.0.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-transformerArea - Transformer / Transpiler

    Type

    Priority

    None yet

    Effort

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions