Reference: https://github.com/microsoft/TypeScript/issues/13462#issuecomment-275860898
interface JsonDeserializable<T> {
fromJson(obj: Object): T;
}
interface JsonSerializable {
toJson(): Object;
}
How to use:
import * as assert from "assert";
class Point2D {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
toJson(): Object {
return this;
}
static fromJson(obj: Object): Point3D {
return new Point3D(obj['x'], obj['y'], obj['z']);
}
}
class Point3D {
x: number;
y: number;
z: number;
constructor(x: number, y: number, z: number) {
this.x = x;
this.y = y;
this.z = z;
}
toJson(): Object {
return this;
}
static fromJson(obj: Object): Point3D {
return new Point3D(obj['x'], obj['y'], obj['z']);
}
}
class Foo {
foo: Point2D;
bar: Point3D;
constructor(foo: Point2D, bar: Point3D) {
this.foo = foo;
this.bar = bar;
}
toJson(): Object {
return {
foo: this.foo.toJson(),
bar: this.bar.toJson()
}
}
static fromJson(obj: Object): Foo {
return new Foo(Point2D.fromJson(obj['foo']), Point3D.fromJson(obj['bar']));
}
}
var DATA: Object = {};
function getJsonData(): Object {
return DATA;
}
function saveJsonData(o: Object) {
DATA = o;
}
// Accepts types that are serializable
function save<T extends JsonSerializable>(o: T) {
const json = o.toJson();
saveJsonData(json);
}
// Accepts types that are deserializable
function loadData<InstanceType>(cls: JsonDeserializable<InstanceType>): InstanceType {
const data = getJsonData();
return cls.fromJson(data);
}
// Accepts types that are both serializable and deserializable
function testSerializationDeserialization<T extends JsonSerializable>(cls: JsonDeserializable<T>, obj: Object) {
const instance = cls.fromJson(obj);
const json = instance.toJson();
assert.deepEqual(json, obj);
}
const foo = new Foo(new Point2D(1,2), new Point3D(1,2,3));
save(foo);
console.log(loadData(Foo)); // Foo object
testSerializationDeserialization(Foo, { foo: { x: 1, y: 2 }, bar: {x: 1, y: 2, z: 3}});