This article is a part of the systemic-ts series.

Source of Complication: Non Bijective JSON Module

JSON data type is limited and JSON.stringify has an incredibly inconsistent quirk. undefined, Function, and Symbol are omitted. NaN and Infinity become null. Map, Set, WeakMap, WeakSet, and WeakRef becomes {}. BigInt throws.

Not only does this quirk limit the utility of JSON but it also necessitates the application to manually select which type can be converted to JSON and which does not, which business object can naturally be persisted as JSON and which requires intermediate transformation; this manual selection is done absolutely manually and no built-in language feature can assist with it.

JSON-able values can be constrained with the following type:

export type JSONValue = string | number | boolean | null | JSONObject | JSONArray;
export type JSONArray = JSONValue[];
export type JSONObject = {
    [key: string]: SerializableValue;
} & {
    [key: symbol]: never;

Deciding to convert a previously non-JSON-able into a JSON-able one means it will never be able to not require a transformation function. However, it is a fair constraint since some data types can never be stringified anyhow (e.g. Function).

It only makes sense that the solution for this quirk lies in the design pattern, not the compilation phase.