How to Create a Simple Typescript Metadata Annotation

后端 未结 1 1931
滥情空心
滥情空心 2021-01-02 03:23

I have some fields that need to be formatted before sent to server-side.

So, I would like to serialize some fields of my typescript classes using custom serializers,

相关标签:
1条回答
  • 2021-01-02 03:54

    My solution below is built on top of:

    1. TypeScript Decorators
    2. Metadata spec (early stage/experimental)

    Prerequisites:

    1. Enable Decorator and decorator metadata support in TypeScript in you tsconfig.json or on the command line:

    tsconfig.json

    {
        "compilerOptions": {
            "target": "es5", // you have to target es5+
            "experimentalDecorators": true,
            "emitDecoratorMetadata": true
            // ....
        }
        // ...
    }
    
    1. Install reflect-metadata package

      npm install --save-dev reflect-metadata

    serializerWith Decorator (factory)

    A decorator factory is something that takes one or many parameters and returns a decorator function that TypeScript uses in the generated JavaScript code.

    I have chosen to directly pass the serialization function into my decorator factory and use the reflect-metadata implementation of the metdata spec to associate the serializer function with the property. I will later retrieve it and use it at run-time.

    function serializeWith(serializer: (input: any) => string) : (target: any, propertyKey: string) => void {
        return function(target: any, propertyKey: string) {
            // serialization here is the metadata key (something like a category)
            Reflect.defineMetadata("serialization", serializer, target, propertyKey);
        }
    }
    

    Use

    Given this serializer:

    function MyDateSerializer(value : any) : string {
        console.log("MyDateSerializer called");
        return "dummy value";
    }
    

    We can then apply the decorator factory like this:

    import "reflect-metadata"; // has to be imported before any decorator which uses it is applied
    
    class Greeter {
        @serializeWith(MyDateSerializer)
        public greeting : string;
    
        constructor(message: string) {
            this.greeting = message;
        }
    }
    

    And we can get and use the serializer like this:

    var greetingInstance = new Greeter("hi");
    
    var serializerFunc : (input: any) => string = Reflect.getMetadata("serialization", greetingInstance, "greeting");
    
    serializerFunc(greetingInstance.greeting);
    

    Sample

    main.ts

    import "reflect-metadata";
    
    function serializeWith(serializer: (input: any) => string) : (target: any, propertyKey: string) => void {
        return function(target: any, propertyKey: string) {
            console.log("serializeWith called: adding metadata");
            Reflect.defineMetadata("serialization", serializer, target, propertyKey);
        }
    }
    
    
    function MyDateSerializer(value : any) : string {
        console.log("MyDateSerializer called");
        return "bla";
    }
    
    class Greeter {
        @serializeWith(MyDateSerializer)
        public greeting : string;
    
        constructor(message: string) {
            console.log("Greeter constructor");
            this.greeting = message;
        }
    }
    
    var greetingInstance = new Greeter("hi");
    
    var serializerFunc : (input: any) => string = Reflect.getMetadata("serialization", greetingInstance, "greeting");
    
    var serializedValue = serializerFunc(greetingInstance.greeting);
    console.log(serializedValue);
    

    Outputs

    c:\code\tmp\lll>node build\main.js
    serializeWith called: adding metadata
    Greeter constructor
    MyDateSerializer called
    bla
    
    0 讨论(0)
提交回复
热议问题