Rich-Harris/devalue

[Feature request] How about serializing arbitrary class instances?

Evertt opened this issue · 1 comments

The way I imagine this could work is as follows:

import * as devalue from 'devalue';
import { User, Post } from '$lib/models';

devalue.registerClasses({ User, Post });

Or otherwise, if you prefer not to offer a global register function, maybe you could add it as an optional last argument to every method that either serializes or parses. Like so:

import * as devalue from 'devalue';
import { User } from '$lib/models';
import * as allModels from '$lib/models';

const user = new User("John Doe");
const data = { user }

let serialized = devalue.uneval(data, allModels);
// or
let serialized = devalue.stringify(data, allModels);

const unserialized = devalue.parse(serialized, allModels);

If needed we can of course enforce that those classes implement a certain interface that may be necessary for the serializing and unserializing process. If something like that is necessary then my preference would go towards enforcing two static methods on the class for (un)serializing. That way it wouldn't pollute the list of the class' instance methods, which is nicer when you're using intellisense / autocomplete in your IDE.

The nice thing about adding a feature like this, is that then we can have objects with functions that can be (un)serialized using this package without it throwing errors. Because if the package knows how to instantiate class objects, then the functions come with it for free.

Edit

If you approve, but don't feel like implementing it any time soon, I could submit a PR. :-)

Edit 2

Here is a working proof of concept in stackblitz. Written with Svelte, simulating server-client communication with components.

It may not be perfect (yet), but maybe it can become perfect with a little more work put into it. I hope you see potential in this.

This is now available — docs on the README. Went with the 'optional last argument' approach, as the SuperJSON-style global registry makes it unreasonably difficult to do certain useful things (e.g. use case in #58, where the reducer/reviver needs to close over local state)