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,
My solution below is built on top of:
tsconfig.json
or on the command line:tsconfig.json
{
"compilerOptions": {
"target": "es5", // you have to target es5+
"experimentalDecorators": true,
"emitDecoratorMetadata": true
// ....
}
// ...
}
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);
}
}
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);
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