125 lines
3.5 KiB
TypeScript
125 lines
3.5 KiB
TypeScript
import { Moment } from "moment";
|
|
|
|
type Simplify<T> = string |
|
|
T extends string ? string :
|
|
T extends number ? number :
|
|
T extends boolean ? boolean :
|
|
T extends Moment ? string :
|
|
T extends Array<infer K> ? Array<Simplify<K>> :
|
|
T extends (infer K)[] ? Simplify<K>[] :
|
|
T extends Object ? Jsonified<T> : any;
|
|
|
|
export type Jsonified<T> = { [K in keyof T]: Simplify<T[K]> }
|
|
export type Optionalify<T> = { [K in keyof T]?: T[K] }
|
|
export type Dictionary<T> = { [key: string]: T };
|
|
|
|
export type Index = string | symbol | number;
|
|
export type FetchingState = 'fetching' | 'ready' | 'error' | 'not-initialized';
|
|
|
|
export function map<T extends {}, KT extends keyof T, R extends { [KR in keyof T] }>(source: T, mapper: (value: T[KT], key: KT) => R[KT]): R {
|
|
const result: R = {} as R;
|
|
|
|
for (const [key, value] of Object.entries(source)) {
|
|
result[key] = mapper(value as T[KT], key as KT);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export function filter<T, KT extends keyof T>(source: T, filter: (value: T[KT], key: KT) => boolean): Optionalify<T> {
|
|
const result: Optionalify<T> = {};
|
|
|
|
for (const [key, value] of Object.entries(source)) {
|
|
if (filter(value as T[KT], key as KT)) {
|
|
result[key] = value;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export function except<T>(source: T, keys: (keyof T)[]) {
|
|
return filter(source, (_, key) => !keys.includes(key))
|
|
}
|
|
|
|
export function only<T>(source: T, keys: (keyof T)[]) {
|
|
return filter(source, (_, key) => keys.includes(key))
|
|
}
|
|
|
|
export function signed(number: number): string {
|
|
return number > 0 ? `+${number}` : number.toString();
|
|
}
|
|
|
|
export function ensureArray<T>(x: T[]|T): T[] {
|
|
return x instanceof Array ? x : [ x ];
|
|
}
|
|
|
|
export function set(object: any, path: string, value: any) {
|
|
const segments = path.split('.');
|
|
while (segments.length > 1) {
|
|
object = object[segments.shift()];
|
|
}
|
|
|
|
object[segments.shift()] = value;
|
|
}
|
|
|
|
export function get(object: any, path: string): any {
|
|
const segments = path.split('.');
|
|
while (segments.length > 1) {
|
|
object = object[segments.shift()];
|
|
}
|
|
|
|
return object[segments.shift()];
|
|
}
|
|
|
|
export function distinct<T>(value: T, index: number, array: T[]) {
|
|
return array.indexOf(value) === index;
|
|
}
|
|
|
|
export function time<T>(action: () => T, name?: string) {
|
|
const start = performance.now();
|
|
const result = action();
|
|
console.debug(`${name || 'this'} operation took ${performance.now() - start}ms`);
|
|
|
|
return result;
|
|
}
|
|
|
|
export const identity = a => a;
|
|
|
|
export function unique<T, U>(array: T[], criterion: (item: T) => U = identity) {
|
|
const result: T[] = [];
|
|
const known = new Set<U>();
|
|
|
|
const entries = array.map(item => [ criterion(item), item ]) as [ U, T ][];
|
|
|
|
for (const [ key, item ] of entries) {
|
|
if (known.has(key)) {
|
|
continue;
|
|
}
|
|
|
|
known.add(key);
|
|
result.push(item);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
type Pattern<TResult, TArgs extends any[]> = [
|
|
(...args: TArgs) => boolean,
|
|
((...args: TArgs) => TResult) | TResult,
|
|
]
|
|
|
|
export function match<TResult, TArgs extends any[]>(...patterns: Pattern<TResult, TArgs>[]): (...args: TArgs) => TResult {
|
|
return (...args: TArgs) => {
|
|
for (let [pattern, action] of patterns) {
|
|
if (pattern(...args)) {
|
|
return typeof action === "function" ? (action as (...args: TArgs) => TResult)(...args) : action;
|
|
}
|
|
}
|
|
|
|
throw new Error(`No pattern matches args: ${JSON.stringify(args)}`);
|
|
}
|
|
}
|
|
|
|
match.default = (...args: any[]) => true;
|