Documentation chapters
Type

Customer serializer

Per default @deepkit/type comes with a JSON serializer and type validation for TypeScript types. You can extend this one and override or remove serialization functionality, or change the way how validation works since validation is attached to the serializer, too.

Create a new serializer

A serializer is just an instance of the class Serializer with registered serializer templates. Serializer templates are little functions that create JavaScript code for the JIT serializer process. Each type (string, number, etc) has its own serializer template that is responsible to return code that does the data conversion or validation. That code has to be compatible to the JavaScript engine a user is using.

Only during the execution of the compiler template function do you (or should you) have full access to the full type. The idea is that you should embed in JavaScript code all information necessary to convert a type in the code directly, leading to code that is highly optimized (aka JIT optimized code).

The following example creates an empty serializer.

import { EmptySerializer } from '@deepkit/type';

class User {
    name: string = '';
    created: Date = new Date;
}

const mySerializer = new EmptySerializer('mySerializer');

const user = deserialize<User>({ name: 'Peter', created: 0 }, undefined, mySerializer);
console.log(user);
$ ts-node app.ts
User { name: 'Peter', created: 0 }
As you can see nothing has been converted (created is still a number, but we defined it as Date). To change that we add a serializer template for the deserialization of type date.
mySerializer.deserializeRegistry.registerClass(Date, (type, state) => {
    state.addSetter(`new Date(${state.accessor})`);
});

const user = deserialize<User>({ name: 'Peter', created: 0 }, undefined, mySerializer);
console.log(user);
$ ts-node app.ts
User { name: 'Peter', created: 2021-06-10T19:34:27.301Z }

Now our serializer converts the value to a Date object.

In order to do the same for serialization, we register another serialization template.

mySerializer.serializeRegistry.registerClass(Date, (type, state) => {
    state.addSetter(`${state.accessor}.toJSON()`);
});

const user1 = new User();
user1.name = 'Peter';
user1.created = new Date('2021-06-10T19:34:27.301Z');
console.log(serialize(user1, undefined, mySerializer));
{ name: 'Peter', created: '2021-06-10T19:34:27.301Z' }

Our new serializer now correctly converts the date from Date object to a string in the serialization process.

Examples

To see many more examples, you can take a look into the code of the JSON serializer included in Deepkit Type.

Extend a serializer

When you want to extend an already existing serializer you can do so using the class inheritance. This works because serializers should be written in a way that they register their templates in the constructor.

class MySerializer extends Serializer {
    constructor(name: string = 'mySerializer') {
        super(name);
        this.registerTemplates();
    }

    protected registerTemplates() {
        this.deserializeRegistry.register(ReflectionKind.string, (type, state) => {
            state.addSetter(`String(${state.accessor})`);
        });

        this.deserializeRegistry.registerClass(Date, (type, state) => {
            state.addSetter(`new Date(${state.accessor})`);
        });

        this.serializeRegistry.registerClass(Date, (type, state) => {
            state.addSetter(`${state.accessor}.toJSON()`);
        });
    }
}
const mySerializer = new MySerializer();
Made in Germany