2019-09-09 10:17:44 -04:00
|
|
|
/**
|
|
|
|
* A function to be passed to JSON.parse as a JSON reviver, in order to transform date values
|
|
|
|
* (which don't have a native JSON representation and therefore are serialized as strings)
|
|
|
|
* back to a JavaScript Date object.
|
|
|
|
* This works by first checking if a string value complies with a date format
|
|
|
|
* and then converting to a Date if and only if that's the case
|
|
|
|
* @param name name of the JSON field to revive
|
|
|
|
* @param value value of the JSON field to revive
|
|
|
|
*/
|
2019-08-30 08:45:55 -04:00
|
|
|
export function dateReviver(name, value) {
|
|
|
|
if (typeof value === "string" && /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.\d\d\dZ$/.test(value)) {
|
|
|
|
return new Date(value);
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
2019-11-26 07:09:27 -05:00
|
|
|
|
|
|
|
|
|
|
|
export function parseJsonOrDefault(jsonString: string) {
|
|
|
|
try {
|
|
|
|
return JSON.parse(jsonString);
|
|
|
|
} catch(error) {
|
|
|
|
console.error(error);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2019-12-03 08:51:16 -05:00
|
|
|
|
|
|
|
export function hasAnyOwnProperty(obj: {}, keys: string[]) {
|
|
|
|
return keys.some(k => obj.hasOwnProperty(k));
|
|
|
|
}
|
2019-12-03 12:30:51 -05:00
|
|
|
|
|
|
|
export function isNullishOrEmpty(obj: any) {
|
|
|
|
return obj == undefined || isEmptyArray(obj);
|
|
|
|
}
|
|
|
|
export function isEmptyArray(obj: any) {
|
|
|
|
return Array.isArray(obj) && obj.length === 0;
|
|
|
|
}
|
2020-01-26 20:01:18 -05:00
|
|
|
|
|
|
|
type AccessorFunction<T, V> = (obj: T) => V;
|
|
|
|
|
|
|
|
type CompareFunction<T> = (a: T, b: T) => number;
|
|
|
|
|
|
|
|
export function numAsc<T, V extends number | bigint>(accessor: AccessorFunction<T, V>): CompareFunction<T>{
|
|
|
|
return (a: T, b: T) => Number(accessor(a) - accessor(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
export function numDesc<T, V extends number | bigint>(accessor: AccessorFunction<T, V>): CompareFunction<T> {
|
|
|
|
return (a: T, b: T) => Number(accessor(b) - accessor(a));
|
|
|
|
}
|
2020-01-31 10:39:47 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* As of Jan 2020, bigint literals e.g. 1n can only be used with TS target esnext
|
|
|
|
* which then doesn't transpile the null conditional/coalescing operators and breaks on Node 12
|
|
|
|
* So BigInt(1) needs to be used here
|
|
|
|
* */
|
|
|
|
export function incBigInt(bigStr: string): string {
|
|
|
|
return bigStr == undefined ? bigStr : String(BigInt(bigStr) + BigInt(1));
|
|
|
|
}
|
|
|
|
export function decBigInt(bigStr: string): string {
|
|
|
|
return bigStr == undefined ? bigStr : String(BigInt(bigStr) - BigInt(1));
|
|
|
|
}
|
2021-01-20 21:27:14 -05:00
|
|
|
|
2021-01-28 21:40:11 -05:00
|
|
|
export function parseBooleanExact(val: string) {
|
|
|
|
if(val === 'true') return true;
|
|
|
|
if(val === 'false') return false;
|
|
|
|
return null;
|
|
|
|
}
|
2021-01-20 21:27:14 -05:00
|
|
|
|
|
|
|
export function pickFields(obj: any, fieldWhitelist: Set<string>) {
|
|
|
|
const subObject = {};
|
|
|
|
for (let [key, value] of Object.entries(obj)) {
|
|
|
|
if(fieldWhitelist.has(key)) {
|
|
|
|
subObject[key] = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return subObject;
|
2021-01-28 21:40:11 -05:00
|
|
|
}
|
2021-03-11 13:33:04 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Generic type for a function validating that the argument is a object with
|
|
|
|
* Used to enforce value types in a config object, but not obscuring the key names
|
|
|
|
* by using a TS lookup type
|
|
|
|
*/
|
|
|
|
type ValueTypeCheck<C> = <K extends string>(x: Record<K, C>) => Record<K, C>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a function that enforces all fields of its argument to be of type C
|
|
|
|
* Useful to create configs where each field must be of a set type,
|
|
|
|
* but the list of keys should be accessible to users of the config variable.
|
|
|
|
*/
|
|
|
|
export function valueType<C>(): ValueTypeCheck<C>{
|
|
|
|
return x => x;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Map all properties of object through a function
|
|
|
|
*/
|
|
|
|
export function mapObject<T, R>(x: T, fn: ([key, value]: [keyof T, T[keyof T]]) => R): Record<keyof T, R> {
|
|
|
|
return Object.assign({}, ...Object.entries(x).map(([key, value]) => ({ [key]: fn([key as keyof T, value]) })) );
|
|
|
|
}
|