import { SDataArray } from 's-array'; import { DataSignal } from 's-js'; export interface Jsonable { toJSON(): any; } /** * Makes an s.js signal jsonable by adding a `toJSON` method that extracts its value during JSONization. * * Adapted from GitHub project * [adamhaile/surplus-todomvc](https://github.com/adamhaile/surplus-todomvc/blob/37ffcdfca66a11365ff88aeed5db9f38b97ba2c6/src/models.ts). */ export function jsonable any>(s: T): T & Jsonable { const augmentedSignal = s as T & Jsonable; augmentedSignal.toJSON = toJSON; return augmentedSignal; } function toJSON(this: () => any): any { const value: any = this(); // A good idea here would be to check if value.toJSON exists. But this is not public API, and we don't need this in // this project. if (value instanceof Set) { return [...value]; } else { return value; } } /** * Creates a new plain JSON value for the given value (which may contain signals or `Set` instances). */ export function toPlain(source: T): Plain { if (typeof source === 'function' && 'toJSON' in source) { return toPlain(source()); } else if (Array.isArray(source)) { return source.map(toPlain) as Plain; } else if (source instanceof Set) { return toPlain(Array.from(source)) as Plain; } else if (typeof source === 'object' && source !== null) { const typedSource: Record = source; const target: Record = {}; for (const key in typedSource) { if (typedSource.hasOwnProperty(key) && key !== 'transient') { const plainValue = toPlain(typedSource[key]); if (plainValue !== undefined) { target[key] = plainValue; } } } return target as Plain; } else { return source as Plain; } } // The following requires at least TypeScript 3.4. See: https://github.com/Microsoft/TypeScript/issues/24622 type PlainHelper = T extends SDataArray ? U1[] : T extends DataSignal ? U2 : T extends Set ? U3[] : T extends ({[P in keyof T]: T[P]} & {transient: any}) ? {[P in Exclude]: PlainHelper} : T extends {[P in keyof T]: T[P]} ? {[P in keyof T]: PlainHelper} : T; export type Plain = PlainHelper>>;