mapconversion.ts
| 1 | //needed to convert ES6 maps from/to json |
| 2 | type SerializablePrimitive = string | number | boolean | null; |
| 3 | type SerializableArray = Serializable[]; |
| 4 | type SerializableObject = { [key: string]: Serializable }; |
| 5 | type Serializable = SerializablePrimitive | SerializableArray | SerializableObject | SerializedMap; |
| 6 | |
| 7 | interface SerializedMap { |
| 8 | __type: "Map"; |
| 9 | value: [Serializable, Serializable][]; |
| 10 | } |
| 11 | |
| 12 | function isSerializedMap(value: unknown): value is SerializedMap { |
| 13 | return ( |
| 14 | typeof value === "object" && |
| 15 | value !== null && |
| 16 | "__type" in value && |
| 17 | (value as SerializedMap).__type === "Map" && |
| 18 | "value" in value && |
| 19 | Array.isArray((value as SerializedMap).value) |
| 20 | ); |
| 21 | } |
| 22 | |
| 23 | function isMap(value: unknown): value is Map<unknown, unknown> { |
| 24 | return value instanceof Map; |
| 25 | } |
| 26 | |
| 27 | function convertMapToSerializable(map: Map<unknown, unknown>): SerializedMap { |
| 28 | const serializedEntries: [Serializable, Serializable][] = []; |
| 29 | |
| 30 | map.forEach((value, key) => { |
| 31 | serializedEntries.push([convertToSerializable(key), convertToSerializable(value)]); |
| 32 | }); |
| 33 | |
| 34 | return { |
| 35 | __type: "Map", |
| 36 | value: serializedEntries, |
| 37 | }; |
| 38 | } |
| 39 | |
| 40 | function convertToSerializable(value: unknown): Serializable { |
| 41 | if (isMap(value)) { |
| 42 | return convertMapToSerializable(value); |
| 43 | } |
| 44 | |
| 45 | if (Array.isArray(value)) { |
| 46 | return value.map((item) => convertToSerializable(item)); |
| 47 | } |
| 48 | |
| 49 | if (typeof value === "object" && value !== null) { |
| 50 | const result: SerializableObject = {}; |
| 51 | for (const [k, v] of Object.entries(value)) { |
| 52 | result[k] = convertToSerializable(v); |
| 53 | } |
| 54 | return result; |
| 55 | } |
| 56 | |
| 57 | return value as Serializable; |
| 58 | } |
| 59 | |
| 60 | function reviveMap(data: SerializedMap): Map<unknown, unknown> { |
| 61 | return new Map(data.value.map(([key, value]) => [reviveFromSerializable(key), reviveFromSerializable(value)])); |
| 62 | } |
| 63 | |
| 64 | function reviveFromSerializable(value: unknown): unknown { |
| 65 | if (isSerializedMap(value)) { |
| 66 | return reviveMap(value); |
| 67 | } |
| 68 | |
| 69 | if (Array.isArray(value)) { |
| 70 | return value.map((item) => reviveFromSerializable(item)); |
| 71 | } |
| 72 | |
| 73 | if (typeof value === "object" && value !== null) { |
| 74 | const result: Record<string, unknown> = {}; |
| 75 | for (const [k, v] of Object.entries(value)) { |
| 76 | result[k] = reviveFromSerializable(v); |
| 77 | } |
| 78 | return result; |
| 79 | } |
| 80 | |
| 81 | return value; |
| 82 | } |
| 83 | |
| 84 | export function stringifyMap(value: unknown): string { |
| 85 | return JSON.stringify(convertToSerializable(value)); |
| 86 | } |
| 87 | |
| 88 | export function parseMap(jsonString: string): unknown { |
| 89 | return reviveFromSerializable(JSON.parse(jsonString)); |
| 90 | } |