Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: Provide additional context about the class to member decorators #466

Open
rbuckton opened this issue Apr 12, 2022 · 5 comments

Comments

@rbuckton
Copy link
Collaborator

rbuckton commented Apr 12, 2022

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

@ArsenyYankovsky
Copy link

ArsenyYankovsky commented Sep 9, 2023

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

@Haixing-Hu
Copy link

@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);
  });
});

@ArsenyYankovsky
Copy link

@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
Copy link
Collaborator Author

@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.

@ImBoop
Copy link

ImBoop commented Feb 5, 2025

What if this were to be bound to with the class prototype that it's applying to. That way it can be applied to static methods, and metadata that is reliant on the class itself (such as storing a list of "approved" methods on the class prototype itself by using an @approved decorator on the methods within the class). There are many libraries that require access to the prototype for referencing/assigning/associating data such as ORMs. (also wait is this a dupe of #517?)

Ugly example:

function Approved(allow){
  console.log("Approving method/property on " + this.name);
  return function(target, context){
    this.approvedMethods.push(allow);
  }
}
class ApprovableClass {
  static approvedMethods = [];
  @Approved('yes')
  function Test(){}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants