tc39/proposal-decorators

Suggestion: Provide additional context about the class to member decorators

Opened this issue · 4 comments

Member decorators (method, accessor, field, etc.) in the current proposal do not have access to the class constructor or prototype, and thus have no access to the name of the class containing the decorated element. This would be valuable information for something like a @logged decorator.

I would propose the addition of either a className property to the context, or something like the class property below:

type Decorator = (value: Input, context: {
  kind: string;
  name: string | symbol;
  access: {
    get?(): unknown;
    set?(value: unknown): void;
  };
  private?: boolean;
  static?: boolean;
  addInitializer?(initializer: () => void): void;

  parent?: {
    kind: "class";
    name: string | undefined;

    // other future class-specific context information such as:
    
    // add a static initializer to the class, even for instance members:
    addInitializer(initializer: () => void): void;
     
    // attach class-specific metadata (separately from function-specific metadata)
    metadata: Record<string | symbol, unknown>;
  };
}) => Output | void;

Related: #465

I want to highlight that is a blocker for a lot of use cases, including several existing popular ORMs (MikroORM, TypeORM etc.).

@rbuckton Actually, we can retrieve the class name from the decorated method. Here's a demonstration:

function Log(target, context) {
  return function (...args) {
    const prototype = Object.getPrototypeOf(this);
    const Class = prototype.constructor;
    const className = Class.name;
    console.debug(`${className}.${context.name}:`, ...args);
    return target.apply(this, args);
  }
}

class Test {
  @Log
  add(x, y) {
    return x + y;
  }
}

describe('Test @Log decorator for class methods', () => {
  test('should work', () => {
    const t = new Test();
    t.add(1, 2);
  });
});

@Haixing-Hu What about the field decorators? Many ORMs (and some validation libraries) collect and save metadata using decorators on class fields and they need access to the class and field names.

@rbuckton Actually, we can retrieve the class name from the decorated method. Here's a demonstration: [...]

That would give you the class name of the instance constructor, not the class name of the class it was declared on, and requires method invocation to access. You could use context.addInitializer, but you that still require an instance be created.