deepkit / deepkit-framework

A new full-featured and high-performance TypeScript framework
https://deepkit.io/
MIT License
3.14k stars 116 forks source link

[type] Feature request: Pass-through options for custom serialize/deserialize/transform #138

Open icopp opened 2 years ago

icopp commented 2 years ago

I've got a complicated setup using @deepkit/type that transforms elaborate nested objects to/from JSON, but also to/from a separate legacy domain-specific language. Some of the data in this second transformation is only kept out-of-band rather than properly being part of the data tree itself (for example, time zones have a separate default value that isn't part of the legacy data representation). If there was a way to pass an object of arbitrary metadata at serialize/deserialize/transform type that was passed down to custom methods, it would be possible to do more or all of this kind of handling entirely as part of the @deepkit/type definitions.

For example, it could look like this in use:

interface LegacyFormatOptions {
  radix: number
}

class Stuff {
  @t.serialize(value => value.toString(radix), 'legacyFormat', { radix }: LegacyFormatOptions) whatever: number
}

...where { radix } is the theoretical metadata object passed into the serialize/deserialize process.

marcj commented 2 years ago

I see multiple ways to do that:

  1. Make a function factory
    
    interface LegacyFormatOptions {
    radix: number
    }

function convertToNumber(options: LegacyFormatOptions) { return value => value.toString(options.radix); }

class Stuff { @t.serialize(convertToNumber({radix: 10})) whatever: number }


2. Custom decorator
```typescript
interface LegacyFormatOptions {
  radix: number
}

function convertToNumber(options: LegacyFormatOptions) {
    return value => value.toString(options.radix);
}

function legacyNumber(radix: number) {
    return t.serialize(convertToNumber({radix}));
}

class Stuff {
  @legacyNumber(10) whatever: number
}
  1. Custom serializer compiler template

interface LegacyFormatOptions {
  radix: number
}

jsonSerializer.fromClass.register('number', (property, state) => {
    const options:LegacyFormatOptions | undefined = property.data['legacy'];
    if (options && options.radix) {
        state.addSetter(`${state.accessor}.toString(${options.radix})`);
    }
});

function legacyNumber(radix: number) {
     return t.data('legacy', {radix});
}
class Stuff {
  @legacyNumber(10) whatever: number
}