Typescript: How to get metadata types info about public fields in class decorator

Viewed 1936

I'm using reflect-metadata lib and Typescript decorators.

I have this class for example (real one will have much more fields):

class Message {
    @FieldTyping
    text: string;

    @FieldTyping
    code: number;

    constructor(text: string, code: number) {
        this.text = text;
        this.code = code;
    }
}

Decorator quite synthetic too, but in real code it doing eventually this:

function FieldTyping(target: Object, key: string) {
    Reflect.getMetadata('design:type', target, key)
    // "String" output in console for "text" key
}

Now it works correctly, but I want to reduce boilerplate. So I want to use one decorator and declare all fields in constructor, like:

@ClassTyping
class Message {
    constructor(public text: string, public code: number) { }
}

Decorator should iterate over all public fields and do the same work as FieldTyping - getting actual type from reflect. So I want two things:

  1. Get all keys from the class. (as I need only string names)
  2. Get right target for Reflect.getMetadata.

But target in the class decorator is a constructor, not Object. I was able to get access to class keys with some "hack":

type ConstructorType<T> = {
    new (...args: any[]): T
}

function ClassTyping<T>(target: ConstructorType<T>) {
    const obj = new target()
    const fields = Object.keys(obj)

    fields.map(prop => Reflect.getMetadata('design:type', target, field))
    // "undefined" output for all fields
}

So I create class instance with undefined values and get it keys (public fields) without knowing its actual type. But I have problem with target. I tried to use any variations in this context instead actual target argument:

  • target.prototype in order to get Object from it constructor;
  • obj as a new instance of this Object;
  • just Message as a type

But none of them worked, I got "undefined" type in all cases. From my experiments it seems like in any other place Reflect.getMetadata don't work too, even without decorators and with simple parameters:

Reflect.getMetadata('design:type', Message, "text")

Apparently it works only with target from field decorator, at least for me. I was digged through whole SO in this theme and didn't find any appropriate solution for my specific purposes.

So please tell if this is possible at all or what I missing.

Note that:

  1. I know it's definitly possible with regex, but I don't want such ugly solution.
  2. It's client code, so I can't get access to filesystem and parse sources to get types with ts-morph, for example (or I missing something, again)
  3. I do have "emitDecoratorMetadata": true in my tsconfig.json
  4. My stack is just NPM and Parcel as bundler. Works with Parcel dev server in local environment and with Harp in "production" (on Heroku).
0 Answers
Related