aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/debug/Test.tsx50
-rw-r--r--src/fields/NewDoc.ts90
2 files changed, 129 insertions, 11 deletions
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 660115453..b46eb4477 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -1,9 +1,35 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { serialize, deserialize, map } from 'serializr';
-import { URLField, Doc } from '../fields/NewDoc';
+import { URLField, Doc, createSchema, makeInterface, makeStrictInterface } from '../fields/NewDoc';
import { SerializationHelper } from '../client/util/SerializationHelper';
+const schema1 = createSchema({
+ hello: "number",
+ test: "string",
+ fields: "boolean",
+ url: URLField,
+ testDoc: Doc
+});
+
+const TestDoc = makeInterface(schema1);
+type TestDoc = makeInterface<typeof schema1>;
+
+const schema2 = createSchema({
+ hello: URLField,
+ test: "boolean",
+ fields: "string",
+ url: "number",
+ testDoc: URLField
+});
+
+const Test2Doc = makeStrictInterface(schema2);
+type Test2Doc = makeStrictInterface<typeof schema2>;
+
+const assert = (bool: boolean) => {
+ if (!bool) throw new Error();
+};
+
class Test extends React.Component {
onClick = () => {
const url = new URLField(new URL("http://google.com"));
@@ -15,10 +41,24 @@ class Test extends React.Component {
doc.url = url;
doc.testDoc = doc2;
- console.log("doc", doc);
- const cereal = SerializationHelper.Serialize(doc);
- console.log("cereal", cereal);
- console.log("doc again", SerializationHelper.Deserialize(cereal));
+
+ const test1: TestDoc = TestDoc(doc);
+ const test2: Test2Doc = Test2Doc(doc);
+ assert(test1.hello === 5);
+ assert(test1.fields === undefined);
+ assert(test1.test === "hello doc");
+ assert(test1.url === url);
+ assert(test1.testDoc === doc2);
+ test1.myField = 20;
+ assert(test1.myField === 20);
+
+ assert(test2.hello === undefined);
+ assert(test2.fields === "test");
+ assert(test2.test === undefined);
+ assert(test2.url === undefined);
+ assert(test2.testDoc === undefined);
+ test2.url = 35;
+ assert(test2.url === 35);
}
render() {
diff --git a/src/fields/NewDoc.ts b/src/fields/NewDoc.ts
index 46f2e20e9..150b8dae8 100644
--- a/src/fields/NewDoc.ts
+++ b/src/fields/NewDoc.ts
@@ -1,5 +1,4 @@
import { observable, action } from "mobx";
-import { Server } from "../client/Server";
import { UndoManager } from "../client/util/UndoManager";
import { serializable, primitive, map, alias } from "serializr";
import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper";
@@ -185,10 +184,6 @@ export class Doc extends RefField {
return new Promise(res => Doc.getField(self, key, ignoreProto, res));
}
- static Serialize(doc: Doc) {
- return SerializationHelper.Serialize(doc[Self]);
- }
-
constructor(id?: string, forceSave?: boolean) {
super(id);
const doc = new Proxy<this>(this, {
@@ -220,4 +215,87 @@ export namespace Doc {
export const Prototype = Symbol("Prototype");
}
-export const GetAsync = Doc.GetAsync; \ No newline at end of file
+export const GetAsync = Doc.GetAsync;
+
+interface IDoc {
+ [key: string]: Field | null | undefined;
+}
+
+interface ImageDocument extends IDoc {
+ data: URLField;
+ test: number;
+}
+
+export type ToType<T> =
+ T extends "string" ? string :
+ T extends "number" ? number :
+ T extends "boolean" ? boolean :
+ T extends { new(...args: any[]): infer R } ? R : undefined;
+
+export type ToInterface<T> = {
+ [P in keyof T]: ToType<T[P]>;
+};
+
+interface Interface {
+ [key: string]: { new(...args: any[]): (ObjectField | RefField) } | "number" | "boolean" | "string";
+}
+
+type FieldCtor<T extends Field> = { new(): T } | "number" | "string" | "boolean";
+
+function Cast<T extends Field>(field: Field | undefined, ctor: FieldCtor<T>): T | undefined {
+ if (field !== undefined) {
+ if (typeof ctor === "string") {
+ if (typeof field === ctor) {
+ return field as T;
+ }
+ } else if (field instanceof ctor) {
+ return field;
+ }
+ }
+ return undefined;
+}
+
+export type makeInterface<T extends Interface> = Partial<ToInterface<T>> & Doc;
+export function makeInterface<T extends Interface>(schema: T): (doc: Doc) => makeInterface<T> {
+ return function (doc: any) {
+ return new Proxy(doc, {
+ get(target, prop) {
+ const field = target[prop];
+ if (prop in schema) {
+ return Cast(field, (schema as any)[prop]);
+ }
+ return field;
+ }
+ });
+ };
+}
+
+export type makeStrictInterface<T extends Interface> = Partial<ToInterface<T>>;
+export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc) => makeStrictInterface<T> {
+ const proto = {};
+ for (const key in schema) {
+ const type = schema[key];
+ Object.defineProperty(proto, key, {
+ get() {
+ return Cast(this.__doc[key], type);
+ },
+ set(value) {
+ value = Cast(value, type);
+ if (value !== undefined) {
+ this.__doc[key] = value;
+ return;
+ }
+ throw new TypeError("Expected type " + type);
+ }
+ });
+ }
+ return function (doc: any) {
+ const obj = Object.create(proto);
+ obj.__doc = doc;
+ return obj;
+ };
+}
+
+export function createSchema<T extends Interface>(schema: T): T {
+ return schema;
+} \ No newline at end of file