diff --git a/src/AsyncData.ts b/src/AsyncData.ts
index 0791feb..40a9039 100644
--- a/src/AsyncData.ts
+++ b/src/AsyncData.ts
@@ -1,6 +1,7 @@
import { keys, values } from "./Dict";
import { Option, Result } from "./OptionResult";
-import { LooseRecord } from "./types";
+import { BOXED_TYPE } from "./symbols";
+import { JsonAsyncData, LooseRecord } from "./types";
import { zip } from "./ZipUnzip";
class __AsyncData {
@@ -92,6 +93,14 @@ class __AsyncData {
// @ts-ignore
value != null && value.__boxed_type__ === "AsyncData";
+ static fromJSON = (value: JsonAsyncData) => {
+ return value.tag === "NotAsked"
+ ? AsyncData.NotAsked()
+ : value.tag === "Loading"
+ ? AsyncData.Loading()
+ : AsyncData.Done(value.value);
+ };
+
map(this: AsyncData, func: (value: A) => B): AsyncData {
if (this === NOT_ASKED || this === LOADING) {
return this as unknown as AsyncData;
@@ -266,6 +275,14 @@ class __AsyncData {
isNotAsked(this: AsyncData): this is NotAsked {
return this === NOT_ASKED;
}
+
+ toJSON(this: AsyncData): JsonAsyncData {
+ return this.match>({
+ NotAsked: () => ({ [BOXED_TYPE]: "AsyncData", tag: "NotAsked" }),
+ Loading: () => ({ [BOXED_TYPE]: "AsyncData", tag: "Loading" }),
+ Done: (value) => ({ [BOXED_TYPE]: "AsyncData", tag: "Done", value }),
+ });
+ }
}
// @ts-expect-error
diff --git a/src/Boxed.ts b/src/Boxed.ts
index 4e19b13..0139ed4 100644
--- a/src/Boxed.ts
+++ b/src/Boxed.ts
@@ -6,3 +6,4 @@ export { Future } from "./Future";
export { Lazy } from "./Lazy";
export { Option, Result } from "./OptionResult";
export * as Serializer from "./Serializer";
+export { JsonAsyncData, JsonOption, JsonResult } from "./types";
diff --git a/src/OptionResult.ts b/src/OptionResult.ts
index 569f606..6d9e2ad 100644
--- a/src/OptionResult.ts
+++ b/src/OptionResult.ts
@@ -1,5 +1,6 @@
import { keys, values } from "./Dict";
-import { LooseRecord } from "./types";
+import { BOXED_TYPE } from "./symbols";
+import { JsonOption, JsonResult, LooseRecord } from "./types";
import { zip } from "./ZipUnzip";
class __Option {
@@ -99,6 +100,10 @@ class __Option {
: a.tag === b.tag;
};
+ static fromJSON = (value: JsonOption) => {
+ return value.tag === "None" ? Option.None() : Option.Some(value.value);
+ };
+
/**
* Returns the Option containing the value from the callback
*
@@ -222,6 +227,13 @@ class __Option {
isNone(this: Option): this is None {
return this === NONE;
}
+
+ toJSON(this: Option): JsonOption {
+ return this.match>({
+ None: () => ({ [BOXED_TYPE]: "Option", tag: "None" }),
+ Some: (value) => ({ [BOXED_TYPE]: "Option", tag: "Some", value }),
+ });
+ }
}
// @ts-expect-error
@@ -387,6 +399,12 @@ class __Result {
return false;
};
+ static fromJSON = (value: JsonResult) => {
+ return value.tag === "Ok"
+ ? Result.Ok(value.value)
+ : Result.Error(value.error);
+ };
+
/**
* Returns the Result containing the value from the callback
*
@@ -522,6 +540,13 @@ class __Result {
isError(this: Result): this is Error {
return this.tag === "Error";
}
+
+ toJSON(this: Result): JsonResult {
+ return this.match>({
+ Ok: (value) => ({ [BOXED_TYPE]: "Result", tag: "Ok", value }),
+ Error: (error) => ({ [BOXED_TYPE]: "Result", tag: "Error", error }),
+ });
+ }
}
// @ts-expect-error
diff --git a/src/Serializer.ts b/src/Serializer.ts
index 742e63d..698f24d 100644
--- a/src/Serializer.ts
+++ b/src/Serializer.ts
@@ -1,34 +1,13 @@
import { AsyncData } from "./AsyncData";
import { Option, Result } from "./OptionResult";
+import { BOXED_TYPE } from "./symbols";
export const encode = (value: any, indent?: number | undefined) => {
return JSON.stringify(
value,
- function (key, value) {
- if (value == null) {
- return;
- }
- if (value.__boxed_type__ === "Option") {
- return {
- __boxed_type__: "Option",
- tag: value.tag,
- value: value.value,
- };
- }
- if (value.__boxed_type__ === "Result") {
- return {
- __boxed_type__: "Result",
- tag: value.tag,
- value: value.value,
- error: value.error,
- };
- }
- if (value.__boxed_type__ === "AsyncData") {
- return {
- __boxed_type__: "AsyncData",
- tag: value.tag,
- value: value.value,
- };
+ (key, value) => {
+ if (typeof value[BOXED_TYPE] === "string") {
+ return { ...value, __boxed_type__: value[BOXED_TYPE] };
}
return value;
},
@@ -42,19 +21,13 @@ export const decode = (value: string) => {
return value;
}
if (value.__boxed_type__ === "Option") {
- return value.tag === "Some" ? Option.Some(value.value) : Option.None();
+ return Option.fromJSON(value);
}
if (value.__boxed_type__ === "Result") {
- return value.tag === "Ok"
- ? Result.Ok(value.value)
- : Result.Error(value.error);
+ return Result.fromJSON(value);
}
if (value.__boxed_type__ === "AsyncData") {
- return value.tag === "NotAsked"
- ? AsyncData.NotAsked()
- : value.tag === "Loading"
- ? AsyncData.Loading()
- : AsyncData.Done(value.value);
+ return AsyncData.fromJSON(value);
}
return value;
});
diff --git a/src/symbols.ts b/src/symbols.ts
new file mode 100644
index 0000000..fc9e6cf
--- /dev/null
+++ b/src/symbols.ts
@@ -0,0 +1 @@
+export const BOXED_TYPE = Symbol.for("__boxed_type__");
diff --git a/src/types.ts b/src/types.ts
index 163c8ab..a85d055 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1 +1,12 @@
export type LooseRecord = Record;
+
+export type JsonResult =
+ | { tag: "Ok"; value: A }
+ | { tag: "Error"; error: E };
+
+export type JsonAsyncData =
+ | { tag: "NotAsked" }
+ | { tag: "Loading" }
+ | { tag: "Done"; value: A };
+
+export type JsonOption = { tag: "None" } | { tag: "Some"; value: A };