@thisisagile/easy
Version:
Straightforward library for building domain-driven microservice architectures
49 lines (38 loc) • 2.57 kB
text/typescript
import { isA } from './IsA';
import { isDefined, isEmpty, isObject } from './Is';
import { Get, ofGet } from './Get';
import { ifDefined } from '../utils/If';
import { TypeGuard } from './TypeGuard';
import { entries } from './Object';
export type JsonValue = string | number | boolean | null | Json | JsonValue[];
export type Json = { [key: string]: JsonValue };
export const isJson: TypeGuard<{ toJSON: () => Json }> = (subject?: unknown): subject is { toJSON: () => Json } =>
isA<{ toJSON: () => Json }>(subject, 'toJSON');
export const json = {
parse: <T extends Json = Json>(subject: unknown): T => JSON.parse(JSON.stringify(subject ?? {})),
merge: (...subjects: unknown[]): Json => json.parse(subjects.map(s => asJson(s, s => json.parse(s))).reduce((js, j) => ({ ...js, ...j }), {})),
delete: <T extends Json = Json>(subject: T, key: string): T => {
ifDefined(subject, () => delete (subject as any)[key]);
return subject;
},
set: <T extends Json = Json>(subject: T, key = '', value?: unknown): T =>
isEmpty(key) ? subject : value !== undefined ? { ...(subject as any), ...{ [key]: value as JsonValue } } : json.delete(subject, key),
omit: <T extends Json = Json>(subject: T, ...keys: string[]): T => keys.reduce((js, k) => json.delete(js, k), json.parse<T>(subject)),
pick: <T extends Json = Json>(subject: T, ...keys: string[]): T => keys.reduce((js, k) => json.set(js, k, (subject as any)[k]), {} as T),
defaults: <T extends Json = Json>(options: Partial<T> = {}, defaults: Partial<T> = {}): T => json.merge(defaults, options) as T,
isSubset: (subject: Json, subset: Json): boolean => !entries(subset).some(([k, v]) => subject[k] !== v),
};
export const toJson = json.merge;
export const asJson = (j?: unknown, alt: Get<Json> = {}): Json => (isJson(j) ? j.toJSON() : isObject(j) ? (j as Json) : ofGet(alt, j));
class Any<T extends Json> {
constructor(readonly value: T) {}
merge = (...subjects: T[]): Any<T> => any<T>(json.merge(this.value, ...subjects) as T);
delete = (key: keyof T): Any<T> => any<T>(json.delete<T>(this.value, key as string));
omit = (...keys: (keyof T)[]): Any<T> => any<T>(json.omit<T>(this.value, ...(keys as string[])));
set = (key: keyof T, value?: unknown): Any<T> => any<T>(json.set(this.value, key as string, value));
toJSON(): Json {
return this.value;
}
}
export const takeFirstKey = (j: unknown, alt: string = ''): string => entries(asJson(j)).first(([, v]) => isDefined(v))?.[0] ?? alt;
export const any = <T extends Json = Json>(value: T): Any<T> => new Any<T>(value);