Last active
February 10, 2021 06:59
-
-
Save reggi/099658fd33d5233afd22c4231480a8eb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| namespace Fox { | |
| export namespace Suppress { | |
| type ThenArg<T> = T extends Promise<infer U> ? U : T | |
| type ReturnAdd<T extends Func, G> = ReturnType<T> extends Promise<infer U> ? Promise<ThenArg<ReturnType<T>> | G> : ReturnType<T> | G | |
| export type Func = (...args: any[]) => any | |
| export type Return<T extends Func> = ReturnAdd<T, undefined> | |
| } | |
| export namespace Use { | |
| type ThenArg<T> = T extends Promise<infer U> ? U : T | |
| type RelatedThen<A, B> = A extends Promise<infer U> ? Promise<B> : B | |
| export type Func = (...args: any[]) => any | |
| export type Return<A extends Func, B> = RelatedThen<ReturnType<A>, B> | |
| export type CallbackValue<A extends Func> = ThenArg<ReturnType<A>> | |
| } | |
| export namespace Promisify { | |
| type ThenArg<T> = T extends Promise<infer U> ? U : T | |
| export type Return<A> = Promise<ThenArg<A>> | |
| } | |
| export namespace First { | |
| export type Func = (...args: any[]) => any | |
| export type Funcs = Func[] | |
| export type Return<T extends Func[]> = ReturnType<T[number]> | |
| export type Params<T extends Func[]> = Parameters<T[number]> | |
| } | |
| export namespace FirstAsync { | |
| type ThenArg<T> = T extends Promise<infer U> ? U : T | |
| export type Func = (...args: any[]) => any | |
| export type Funcs = Func[] | |
| export type Params<T extends Func[]> = Parameters<T[number]> | |
| export type Return<T extends Func[]> = Promise<ThenArg<ReturnType<T[number]>>> | |
| } | |
| export namespace All { | |
| export type Func = (...args: any[]) => any | |
| export type Funcs = Func[] | |
| type ReturnTuple<T extends readonly Func[]> = { | |
| [K in keyof T]: T[K] extends Func ? ReturnType<T[K]> : never | |
| } | |
| export type Params<T extends Func[]> = Parameters<T[number]> | |
| export type Return<T extends readonly Func[]> = ReturnTuple<T> | |
| } | |
| export namespace AllAsync { | |
| type ThenArg<T> = T extends Promise<infer U> ? U : T | |
| export type Func = (...args: any[]) => any | |
| export type Funcs = Func[] | |
| export type TupleRmPromise<T extends readonly Func[]> = { | |
| [K in keyof T]: T[K] extends Func ? ThenArg<ReturnType<T[K]>> : never | |
| } | |
| export type Params<T extends Func[]> = Parameters<T[number]> | |
| export type Return<T extends readonly Func[]> = Promise<TupleRmPromise<T>> | |
| } | |
| export namespace Lead { | |
| export type Func = (args: any) => any | |
| export type LeadParamSingle<T extends Func> = Parameters<T>['length'] extends 1 ? Parameters<T>[0] : never | |
| export type Params<S extends string, F extends Func> = Record<S, LeadParamSingle<F>> | |
| } | |
| export namespace Deundefy { | |
| export type Func = (...args: any[]) => any | |
| export type Deundefy<T> = Exclude<T, undefined> | |
| export type DeundefyPromise<T> = T extends Promise<infer U> ? Promise<Deundefy<U>> : Deundefy<T> | |
| export type Return<T extends Func> = DeundefyPromise<ReturnType<T>> | |
| } | |
| export namespace Undefy { | |
| export type Func = (...args: any[]) => any | |
| export type UndefyPromise<T> = T extends Promise<infer U> ? Promise<undefined> : undefined | |
| export type Return<T extends Func> = UndefyPromise<ReturnType<T>> | |
| } | |
| export namespace Flow { | |
| export type Func = (...args: any[]) => any | |
| export type Funcs = readonly Func[] | |
| type FuncPass<A, B> = (x: A) => B | |
| type Tail<T extends readonly FuncPass<any, any>[]> = FuncPass<T, void> extends ((h: any, ...r: infer R) => void) ? R : never; | |
| export type Check<T extends readonly FuncPass<any, any>[]> = { [K in keyof T]: | |
| K extends keyof Tail<T> ? ( | |
| [T[K], Tail<T>[K]] extends [FuncPass<infer A, infer R>, FuncPass<infer S, any>] ? ( | |
| [R] extends [S] ? T[K] : FuncPass<A, S> | |
| ) : never | |
| ) : T[K] | |
| } | |
| type FirstFnParam<Fns extends Funcs> = Parameters<Fns[0]>[0] | |
| export type Params<T extends Funcs> = FirstFnParam<T> | |
| type LengthOfTuple<T extends Funcs> = T extends { length: infer L } ? L : never; | |
| type DropFirstInTuple<T extends Funcs> = ((...args: T) => any) extends (arg: any, ...rest: infer U) => any ? U : T; | |
| type LastInTuple<T extends Funcs> = T[LengthOfTuple<DropFirstInTuple<T>>]; | |
| type LastFnReturns<Fns extends Funcs> = ReturnType<LastInTuple<Fns>> | |
| export type Return<T extends Funcs> = LastFnReturns<T> | |
| } | |
| export namespace Journey { | |
| // NOTE Journey's have one optional arg | |
| export type Func = (a?: any) => any | |
| export type Funcs = Func[] | |
| } | |
| export namespace JourneyKeyed { | |
| // NOTE Journey's have one optional arg | |
| export type Func = (a: any) => any | |
| export type Funcs = { [k: string]: Func } | |
| } | |
| export namespace CliffFlat { | |
| export type Func = (...args: any[]) => any | |
| export type Funcs = { [k: string]: Func } | |
| } | |
| export namespace CliffKeyed { | |
| export type Func = (...args: any[]) => any | |
| export type Funcs = { [k: string]: Func } | |
| } | |
| export namespace Pick { | |
| type ValuesToTuple<ObjT extends object, KeysT extends readonly (keyof ObjT)[]> | |
| = { [index in keyof KeysT]: ObjT[Extract<KeysT[index], keyof ObjT>] } | |
| export type Return<A extends object, B extends readonly (keyof A)[]> = ValuesToTuple<A, B> | |
| } | |
| export namespace Choosy { | |
| export type Func = (...args: any[]) => any | |
| export type Funcs = { [k: string]: Func } | |
| export type ObjectReturn<T extends Funcs> = { [K in keyof T]: ReturnType<T[K]> } | |
| export type Return<T extends Funcs, I> = Pick<ObjectReturn<T>, keyof T & keyof I> | |
| export type Params<T extends Funcs> = { [K in keyof T]: Parameters<T[K]>[0] } | |
| } | |
| export namespace ChoosySpread { | |
| export type Func = (...args: any[]) => any | |
| export type Funcs = { [k: string]: Func } | |
| export type ObjectReturn<T extends Funcs> = { [K in keyof T]: ReturnType<T[K]> } | |
| export type Return<T extends Funcs, I> = Pick<ObjectReturn<T>, keyof T & keyof I> | |
| export type Params<T extends Funcs> = { [K in keyof T]: Parameters<T[K]> } | |
| // export type Params <T extends Funcs, G extends string> = G extends 'SINGLE' ? ParamsSingle<T> : ParamsSpread<T> | |
| } | |
| export namespace Parameterize { | |
| export type Func = (...args: any[]) => any | |
| export type Funcs = Func[] | |
| type ZipTuple<T extends readonly any[], U extends readonly any[]> = { | |
| [K in keyof T]: [T[K], K extends keyof U ? U[K] : never] | |
| } | |
| type KeyValTuplesToObject<K extends readonly PropertyKey[], V extends readonly any[]> = | |
| ZipTuple<K, V>[number] extends infer Z ? | |
| [Z] extends [[any, any]] ? { [P in Z[0]]: Extract<Z, [P, any]>[1] } : never : never | |
| export type Param<A extends Func, B extends readonly any[]> = KeyValTuplesToObject<B, Parameters<A>> | |
| } | |
| // export namespace Profound { | |
| // type FirstArg<T extends any> = | |
| // T extends [infer R, ...any[]] ? R : | |
| // T extends [] ? undefined : | |
| // T; | |
| // type ThenArg<T> = T extends Promise<infer U> ? U : T | |
| // export type Func = (...args: any[]) => any | |
| // export type Funcs = { [k: string]: Func } | |
| // export type Callback<T extends Funcs> = { [K in keyof T]: FirstArg<ThenArg<ReturnType<T[K]>>> } | |
| // type AllReturn<T extends Funcs> = { [K in keyof T]: FirstArg<ThenArg<ReturnType<T[K]>>> } | |
| // type FirstParam<T extends Funcs> = Parameters<T[keyof T]>[0] | |
| // export type Param<T extends Funcs> = AllReturn<T> | FirstParam<T> | |
| // } | |
| // export namespace ProfoundFix { | |
| // type FirstArg<T extends any> = | |
| // T extends [infer R, ...any[]] ? R : | |
| // T extends [] ? undefined : | |
| // T; | |
| // type ThenArg<T> = T extends Promise<infer U> ? U : T | |
| // export type Funcs = { [k: string]: any } | |
| // export type CallbackArgs<T extends Funcs> = { [K in keyof T]: FirstArg<ThenArg<ReturnType<T[K]>>> } | |
| // type UnionToIntersectionValues<U, K extends keyof U = keyof U> = | |
| // (K extends unknown ? (k: U[K]) => void : never) extends ((k: infer I) => void) ? I : never | |
| // export type Param<T extends Funcs> = UnionToIntersectionValues<{ | |
| // [K in keyof T]: | |
| // K extends string ? { [KK in K]: ThenArg<ReturnType<T[K]>> } | Parameters<T[K]>[0] : never | |
| // }> | |
| // export type Func = (...args: any) => any | |
| // // type CallbackReturn<T> = T extends Func ? ThenArg<ReturnType<T>> : never | |
| // type IsPromise<T> = T extends Promise<infer U> ? true : null | |
| // type FuncReturnsPromise<T extends Func> = IsPromise<ReturnType<T>> | |
| // type ObjectHasPromise<T> = { | |
| // [K in keyof T]: T[K] extends Func ? ReturnType<T[K]> extends Promise<infer U> ? true : null : null | |
| // }[keyof T] | |
| // type ObjectPromises<T> = { | |
| // [K in keyof T]: T[K] extends Func ? ReturnType<T[K]> extends Promise<infer U> ? true : null : null | |
| // } | |
| // // type HasPromise<Fns, Cb extends Func> = ObjectHasPromise<Fns> | FuncReturnsPromise<Cb> | |
| // type ObjectReturns<T> = { | |
| // [K in keyof T]: T[K] extends Func ? ThenArg<ReturnType<T[K]>> : never | |
| // } | |
| // type ObjectHasPromises<Fns, Params> = Omit<ObjectPromises<Fns>, keyof Params>[keyof Omit<ObjectPromises<Fns>, keyof Params>] | |
| // type HasPromiseDirect<Fns, R, Params> = ObjectHasPromises<Fns, Params> | IsPromise<R> | |
| // export type HasPromise<Fns, R, Params> = HasPromiseDirect<Fns, R, Params> | IsPromise<R> | |
| // // export type Return<Fns, Cb extends Func | undefined, Params> = HasPromise<Fns, Cb, Params> extends null ? ThenArg<ReturnType<Cb>> : Promise<ThenArg<ReturnType<Cb>>> | |
| // // HasPromiseDirect<Fns, R> extends null ? ThenArg<R> : Promise<ThenArg<R>> | |
| // // we set up return type based on if there's a promise in the funcs obj or the callback is a promise | |
| // // we need two extensions to this :( | |
| // // 1. if the key of the promise was passed into Params, it's not a promise anymore | |
| // // 2. if the callback is not present, the return is set to the return value of all keys (promise depending) | |
| // type SingleArgFn = (a: any) => any | |
| // export type Return<FNS, CB, IN> = | |
| // CB extends SingleArgFn ? | |
| // HasPromise<FNS, ReturnType<CB>, IN> extends null ? | |
| // ThenArg<ReturnType<CB>> : | |
| // Promise<ThenArg<ReturnType<CB>>> : | |
| // ObjectReturns<FNS> | |
| // } | |
| export namespace Profound { | |
| export type Func = (...arg: any) => any | |
| export type SingleArgFunc = (arg: any) => any | |
| export type SingleArgFuncs = { [k: string]: SingleArgFunc } | |
| export namespace Params { | |
| type Func = (arg: any) => any | |
| type Funcs = { [k: string]: Func } | |
| type FirstArg<T extends any> = | |
| T extends [infer R, ...any[]] ? R : | |
| T extends [] ? undefined : | |
| T; | |
| type ThenArg<T> = T extends Promise<infer U> ? U : T | |
| type UnionToIntersectionValues<U, K extends keyof U = keyof U> = | |
| ([K] extends [never] | |
| ? unknown | |
| : K extends unknown | |
| ? (k: U[K]) => void | |
| : never | |
| ) extends (k: infer I) => void ? I : never | |
| export type FnsBoth<T extends Funcs> = UnionToIntersectionValues<{ | |
| [K in keyof T]: Parameters<T[K]>[0] extends undefined ? | |
| { [KK in K]?: ThenArg<ReturnType<T[K]>> } : | |
| { [KK in K]: ThenArg<ReturnType<T[K]>> } | FirstArg<Parameters<T[K]>> | |
| }> | |
| export type Keys<T> = { [K in keyof T]: T[K] }[keyof T] | |
| export type ObjEmptyNever<T> = Keys<T> extends never ? never : T | |
| export type ObjKeysWithoutNever<T> = { [K in keyof T]: T[K] extends never ? never : K }[keyof T] | |
| export type ObjWithoutNever<T> = Pick<T, ObjKeysWithoutNever<T>>; | |
| export type ObjClean<T> = ObjEmptyNever<ObjWithoutNever<T>> | |
| export type FnsOverride<T> = { [K in keyof T]: T[K] extends Func ? ThenArg<ReturnType<T[K]>> : never } | |
| export type FnArguments<T> = ObjClean<{ [K in keyof T]: T[K] extends (arg: any) => any ? Parameters<T[K]>[0] extends undefined ? never : Parameters<T[K]>[0] : never }> | |
| export type Value<T extends Funcs> = FnArguments<T> extends never ? FnsBoth<T> | void : FnsBoth<T> | |
| // export type Value<T> = FnsBoth<T> | |
| } | |
| export namespace Callback { | |
| type ThenArg<T> = T extends Promise<infer U> ? U : T | |
| export type ObjReturn<T> = { [K in keyof T]: T[K] extends SingleArgFunc ? ThenArg<ReturnType<T[K]>> : never } | |
| } | |
| export namespace Return { | |
| export type IsPromise<T> = T extends Promise<infer I> ? T : never | |
| export type FnIsPromise<T> = T extends (...args: any) => any ? IsPromise<ReturnType<T>> : never | |
| export type FnIsPromiseFn<T> = T extends (...args: any) => any ? IsPromise<ReturnType<T>> extends never ? never : T : never | |
| // gets object keys in object, never if no keys | |
| export type Keys<T> = { [K in keyof T]: T[K] }[keyof T] | |
| // get object if has keys, never if no keys | |
| export type ObjEmptyNever<T> = Keys<T> extends never ? never : T | |
| // gets object keys without value set to never | |
| export type ObjKeysWithoutNever<T> = { [K in keyof T]: T[K] extends never ? never : K }[keyof T] | |
| // gets object without never keys | |
| export type ObjWithoutNever<T> = Pick<T, ObjKeysWithoutNever<T>>; | |
| // gets functions that return promises in object never if empty, without never properties | |
| export type ObjFnIsPromise<T> = ObjEmptyNever<ObjWithoutNever<{ [K in keyof T]: FnIsPromise<T[K]> }>> | |
| // removes keys from B in A | |
| export type ObjDiff<A, B> = Omit<A, keyof B>[keyof Omit<A, keyof B>] | |
| // gets promises from A and removes them if theyr'e in C | |
| export type ObjPromiseDiff<A, C> = ObjFnIsPromise<A> extends never ? never : ObjDiff<ObjFnIsPromise<A>, C> | |
| // if B returns promise, or if there are promises in A and don't match keys in C | |
| export type RetunsPromise<A, B, C> = FnIsPromiseFn<B> | ObjPromiseDiff<A, C> | |
| // if promise, provides inner promise type | |
| type ThenArg<T> = T extends Promise<infer U> ? U : T | |
| // gets return type from function, withou inner promise if any | |
| type ReturnValue<T extends SingleArgFunc> = T extends (args: any) => any ? ThenArg<ReturnType<T>> : never | |
| // gets all return types in an object of functions | |
| type ObjReturn<T extends SingleArgFuncs> = { [K in keyof T]: ReturnValue<T[K]> } | |
| // checks A diffed with C for promsies, if so wraps all ObjectReturns in promise | |
| type ReturnNoCallback<A extends SingleArgFuncs, B, C> = ObjPromiseDiff<A, C> extends never ? ObjReturn<A> : Promise<ObjReturn<A>> | |
| // checks for promise, returns value of inner callback wrapped in promise | |
| type ReturnYesCallback<A, B extends SingleArgFunc, C> = RetunsPromise<A, B, C> extends never ? ReturnValue<B> : Promise<ReturnValue<B>> | |
| // checks if callback is available, before deligation of return | |
| // export type Value<A, B, C> = unknown extends B ? ReturnNoCallback<A, B, C> : ReturnYesCallback<A, B, C> | |
| // for some reason have to check if B actually returns something because `B` is known | |
| export type Value<A extends SingleArgFuncs, B extends SingleArgFunc, C> = unknown extends ReturnValue<B> ? ReturnNoCallback<A, B, C> : ReturnYesCallback<A, B, C> | |
| } | |
| } | |
| export namespace Tap { | |
| export type Func = (...args: any[]) => any | |
| export type RelatedThen<A, B> = A extends Promise<infer U> ? Promise<B> : B | |
| export type Return<A extends Func, B> = RelatedThen<ReturnType<A>, B> | |
| } | |
| export namespace Acrew { | |
| export type PlainObject = { [name: string]: any } | |
| export type Func = (a: any) => any | |
| export type Funcs = { [name: string]: Func } | |
| type FirstArg<T extends any> = | |
| T extends [infer R, ...any[]] ? R : | |
| T extends [] ? undefined : | |
| T; | |
| type ThenArg<T> = T extends Promise<infer U> ? U : T | |
| export type Callback<T extends Funcs> = { [K in keyof T]: FirstArg<ThenArg<ReturnType<T[K]>>> } | |
| type AllReturn<T extends Funcs> = { [K in keyof T]: FirstArg<ThenArg<ReturnType<T[K]>>> } | |
| type FirstParam<T extends Funcs> = Parameters<T[keyof T]>[0] | |
| export type Param<T extends Funcs> = AllReturn<T> | FirstParam<T> | |
| export type ObjectReturn<T extends Funcs> = { [K in keyof T]: ReturnType<T[K]> } | |
| export type Return<A extends Funcs, G> = ObjectReturn<A> & G | |
| } | |
| // NOTE: ----- Cool But Depreacated types ----- | |
| // NOTE: This is cool returns { name: number, plan: [string, number]} based on how many args there are | |
| // NOTE: But there's no way of knowing if an array is one arg arg or a flat one, so I can't use it YET! | |
| // type LeadParam<T extends Func> = Parameters<T>['length'] extends 1 ? Parameters<T>[0] : Parameters<T> | |
| // type KeysHasProps<T extends Funcs> = { [K in keyof T]: T[K] extends Func ? LeadParam<T[K]> extends [] ? never : K : never }[keyof T] | |
| // type HasProps<T extends Funcs> = Pick<T, KeysHasProps<T>> | |
| // NOTE: Checks to see if there is more than one input, if so it's a tuple. | |
| // export type Params<T extends Funcs> = { [K in keyof HasProps<T>]: LeadParam<T[K]> } | |
| // NOTE: This was also a cool idea, conditionaly setting return type based on input | |
| // export type Params <T extends Funcs, G extends string> = G extends 'SINGLE' ? ParamsSingle<T> : ParamsSpread<T> | |
| } | |
| export class Fox { | |
| // NOTE: Returns type `void` | |
| static void = (() => { })() | |
| // NOTE: Checkes obj is unresolved promise | |
| static isPromise(obj: any) { | |
| return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function' | |
| } | |
| // NOTE: Supresses errors by catching them, and returning undefined instead | |
| static suppress<T extends Fox.Suppress.Func>(fn: T) { | |
| return (...input: Parameters<T>): Fox.Suppress.Return<T> => { | |
| return Fox.use(fn, (err, value) => { | |
| if (err) return undefined | |
| return value | |
| })(...input) as unknown as Fox.Suppress.Return<T> | |
| } | |
| } | |
| // NOTE: Wraps any function provides callback value, returns promise or sync function with modified result | |
| static use<A extends Fox.Use.Func, G>(fn: A, callback: (e: Error | null, a: Fox.Use.CallbackValue<A>) => G) { | |
| return (...args: Parameters<A>): Fox.Use.Return<A, G> => { | |
| try { | |
| const v = fn(...args as any[]) | |
| if (Fox.isPromise(v)) { | |
| return v.then((v: any) => callback(null, v)).catch((e: any) => callback(e, undefined as Fox.Use.CallbackValue<A>)) | |
| } | |
| return callback(null, v) as Fox.Use.Return<A, G> | |
| } catch (e) { | |
| return callback(e, undefined as Fox.Use.CallbackValue<A>) as unknown as Fox.Use.Return<A, G> | |
| } | |
| } | |
| } | |
| // NOTE: Wraps any function asserts async updates return type | |
| static promisify<A extends any[], P>(func: (...args: A) => P) { | |
| return async (...args: A) => { | |
| return func(...args) as unknown as Fox.Promisify.Return<P> | |
| } | |
| } | |
| // NOTE: Runs fn[] returns first returned value where function did not throw, may or may not return promise | |
| static first<A extends Fox.First.Funcs, B extends Fox.First.Return<A>>(...fns: A) { | |
| return (...input: Fox.First.Params<A>): B => { | |
| return fns.reduce((acq: (...args: any) => any, fn: (...args: any) => any) => { | |
| return Fox.use(acq, (acqError, acqValue) => { | |
| if (acqValue) return acqValue | |
| return Fox.use(fn, (fnError, fnValue) => { | |
| if (fnError) throw fnError | |
| if (fnValue) return fnValue | |
| })(...input as any[]) | |
| }) | |
| }, Fox.error(new Error('No Value')))() | |
| } | |
| } | |
| // // NOTE Example | |
| // static example = (() => { | |
| // const alpha = (number: number) => number + 3 | |
| // const beta = (number: number) => number + 3 | |
| // const gamma = (number: number) => number + 3 | |
| // const a = Fox.first(alpha, beta, gamma) | |
| // const b = Fox.firstAsync(alpha, beta, gamma) | |
| // b(1).then(console.log) | |
| // })() | |
| // NOTE: Runs fn[] asserts that returned value must be promise | |
| static firstAsync<A extends Fox.FirstAsync.Funcs>(...fns: A) { | |
| return (...input: Fox.FirstAsync.Params<A>) => { | |
| return Fox.promisify(Fox.first(...fns))(...input) as unknown as Fox.FirstAsync.Return<A> | |
| } | |
| } | |
| // NOTE Runs fn[] provides input to all fn[] and runs them, may or may not return unresolved promise | |
| static all<T extends Fox.All.Funcs>(...funcs: T) { | |
| return (...input: Fox.All.Params<T>) => { | |
| return funcs.map((fn) => { | |
| return Fox.use(fn, (err, value) => { | |
| if (err) throw err | |
| return value | |
| })(...input) | |
| }) as Fox.All.Return<T> | |
| } | |
| } | |
| // NOTE Runs fn[] same as all, resolves all promises | |
| static allAsync<T extends Fox.AllAsync.Funcs>(...funcs: T) { | |
| return (...input: Fox.AllAsync.Params<T>) => { | |
| return Promise.all(Fox.all(...funcs)(...input)) as Fox.AllAsync.Return<T> | |
| } | |
| } | |
| // NOTE lead('hello', (a) => b) => ({ hello: a}) => b | |
| static lead<S extends string, F extends Fox.Lead.Func>(string: S, fn: F, e?: Error) { | |
| return (input: Fox.Lead.Params<S, F>): ReturnType<F> => { | |
| if (e && (input === undefined || !input.hasOwnProperty(string))) throw e || new Error(`No ${string} available`) | |
| return fn(input[string]) | |
| } | |
| } | |
| // NOTE Example | |
| // const meow = (name: string) => `${name} says meow` | |
| // const meowLead = Fox.lead('name', meow, new Error('Missing name')) | |
| // const v = meowLead({ name: 'Mittens' }) | |
| // console.log(v) | |
| // NOTE lead('hello', (a, b) => c) => ({ hello: [a, b]}) => c | |
| static leadArr<S extends string, F extends (...a: any[]) => any>(string: S, fn: F, e: Error) { | |
| return (input: Record<S, Parameters<F>>): ReturnType<F> => { | |
| if (e && (input === undefined || !input.hasOwnProperty(string))) throw e | |
| return fn(...input[string]) | |
| } | |
| } | |
| // NOTE [() => undefined | user] => [() => user] | |
| static deundefy<T extends Fox.Deundefy.Func>(func: T, err: Error) { | |
| return (...args: Parameters<T>) => { | |
| return Fox.use(func, (error, value) => { | |
| if (error) throw error | |
| if (value === undefined) throw err | |
| return value | |
| })(...args) as unknown as Fox.Deundefy.Return<T> | |
| } | |
| } | |
| // NOTE [() => user] => [() => user | undefined] | |
| static undefy<T extends Fox.Undefy.Func>(func: T) { | |
| return (...args: Parameters<T>) => { | |
| return Fox.use(func, (error, value) => { | |
| if (error) return undefined | |
| throw error | |
| })(...args) as unknown as Fox.Undefy.Return<T> | |
| } | |
| } | |
| // NOTE Returns a function that throws an error | |
| static error(e: Error) { | |
| return () => { | |
| throw e | |
| } | |
| } | |
| // NOTE Returns a function returns passed in value | |
| static nest<T>(value: T): () => T { | |
| return () => { | |
| return value | |
| } | |
| } | |
| // NOTE comperable to lodash.flow but with async and proper typing | |
| static flow<T extends Fox.Flow.Funcs>(...funcs: Fox.Flow.Check<T>) { | |
| return (input: Fox.Flow.Params<T>) => { | |
| return funcs.reduce((acq: Fox.Flow.Func, fn: Fox.Flow.Func) => { | |
| return Fox.use(acq, (acqError, acqValue) => { | |
| if (acqError) throw acqError | |
| return Fox.use(fn, (fnError, fnValue) => { | |
| if (fnError) throw fnError | |
| return fnValue | |
| })(acqValue) | |
| }) | |
| }, Fox.nest(input))(input) as Fox.Flow.Return<T> | |
| } | |
| } | |
| // NOTE Reverses an array, pretty standard | |
| static reverseArray(arr: any[]) { | |
| var newArray: any[] = []; | |
| for (var i = arr.length - 1; i >= 0; i--) { | |
| newArray.push(arr[i]); | |
| } | |
| return newArray; | |
| } | |
| // NOTE Returns a tuple | |
| static tuple<T extends readonly any[]>(...args: T) { | |
| return args | |
| } | |
| // NOTE Reassigns arguments (FN, ({ a }) => Fox.tuple([])) | |
| static reassignArgs = <A extends (...args: any[]) => any, BIn extends any[]>(a: A, b: (...a: BIn) => Parameters<A>) => { | |
| return (...input: BIn): ReturnType<A> => { | |
| return a(...b(...input)) | |
| } | |
| } | |
| // NOTE Recreation of array'd journey | |
| static journey(funcs: Fox.Journey.Funcs) { | |
| return (input: any = {}) => { | |
| return funcs.reduce((acq: Fox.Journey.Func, fn: Fox.Journey.Func) => { | |
| return Fox.use(acq, (acqError, acqValue) => { | |
| if (acqError) throw acqError | |
| return Fox.use(fn, (fnError, fnValue) => { | |
| if (fnError) throw fnError | |
| return { ...acqValue, ...fnValue } | |
| })(acqValue) | |
| }) | |
| }, Fox.nest(input))(input) | |
| } | |
| } | |
| // NOTE Recreation of keye'd journey | |
| static journeyKeyed(funcs: Fox.JourneyKeyed.Funcs) { | |
| return (input: any = {}) => { | |
| const keys = Object.keys(funcs) | |
| return keys.reduce((acq: Fox.JourneyKeyed.Func, key: string) => { | |
| return Fox.use(acq, (acqError, acqValue) => { | |
| if (acqError) throw acqError | |
| return Fox.use(funcs[key], (fnError, fnValue) => { | |
| if (fnError) throw fnError | |
| return { ...acqValue, [key]: fnValue } | |
| })(acqValue) | |
| }) | |
| }, Fox.nest(input))(input) | |
| } | |
| } | |
| // NOTE standard pick function [({a, b, c}), 'a', 'c'] => {a, c} | |
| static pick<T extends object, B extends (keyof T)[]>(o: T, ...keys: B) { | |
| return keys.map(key => o[key]) as unknown as Fox.Pick.Return<T, B> | |
| } | |
| // NOTE Passes all args into all functions | |
| static cliffFlat(funcs: Fox.CliffFlat.Funcs) { | |
| return (...input: any) => { | |
| const funcKeys = Object.keys(funcs) | |
| return funcKeys.reduce((acq: Fox.CliffFlat.Func, key: string) => { | |
| return Fox.use(acq, (acqError, acqValue) => { | |
| if (acqError) throw acqError | |
| return Fox.use(funcs[key], (fnError, fnValue) => { | |
| if (fnError) throw fnError | |
| return { ...acqValue, [key]: fnValue } | |
| })(...input) | |
| }) | |
| }, Fox.nest({}))() | |
| } | |
| } | |
| // NOTE Passes key'd args into key'd functions | |
| static cliffKeyed(funcs: Fox.CliffKeyed.Funcs) { | |
| return (input: any) => { | |
| const funcKeys = Object.keys(funcs) | |
| return funcKeys.reduce((acq: Fox.CliffKeyed.Func, key: string) => { | |
| return Fox.use(acq, (acqError, acqValue) => { | |
| if (acqError) throw acqError | |
| if (!input[key]) throw new Error(`Missing input key ${key}`) | |
| return Fox.use(funcs[key], (fnError, fnValue) => { | |
| if (fnError) throw fnError | |
| return { ...acqValue, [key]: fnValue } | |
| })(input[key]) | |
| }) | |
| }, Fox.nest({}))() | |
| } | |
| } | |
| // NOTE Only runs functions where input keys match | |
| static choosy<A extends Fox.Choosy.Funcs, B extends Partial<Fox.Choosy.Params<A>>>(funcs: A) { | |
| return (input: B): Fox.Choosy.Return<A, B> => { | |
| const inputKeys = Object.keys(input) | |
| return inputKeys.reduce((acq: Fox.Choosy.Func, key: string) => { | |
| return Fox.use(acq, (acqError, acqValue) => { | |
| if (acqError) throw acqError | |
| if (!input[key]) throw new Error(`Missing input key ${key}`) | |
| return Fox.use(funcs[key], (fnError, fnValue) => { | |
| if (fnError) throw fnError | |
| return { ...acqValue, [key]: fnValue } | |
| })(input[key]) | |
| }) | |
| }, Fox.nest({}))() | |
| } | |
| } | |
| // static example = (() => { | |
| // // NOTE Example | |
| // Fox.choosy({ | |
| // }) | |
| // })() | |
| // NOTE Only runs functions where input keys match | |
| static choosySpread<A extends Fox.ChoosySpread.Funcs, B extends Partial<Fox.ChoosySpread.Params<A>>>(funcs: A) { | |
| return (input: B): Fox.ChoosySpread.Return<A, B> => { | |
| const inputKeys = Object.keys(input) | |
| return inputKeys.reduce((acq: Fox.ChoosySpread.Func, key: string) => { | |
| return Fox.use(acq, (acqError, acqValue) => { | |
| if (acqError) throw acqError | |
| if (!input[key] || !Array.isArray(input[key])) throw new Error(`Missing input key ${key}`) | |
| return Fox.use(funcs[key], (fnError, fnValue) => { | |
| if (fnError) throw fnError | |
| return { ...acqValue, [key]: fnValue } | |
| })(...input[key] as any[]) | |
| }) | |
| }, Fox.nest({}))() | |
| } | |
| } | |
| static parameterize<A extends Fox.Parameterize.Func, B extends string[]>(fn: A, ...keys: B) { | |
| return (input: Fox.Parameterize.Param<A, B>): ReturnType<A> => { | |
| const pass = keys.map((value: string) => { | |
| if (input[value]) return input[value] | |
| throw new Error('Missing Parameter key ${key}') | |
| }) | |
| return fn(...pass) | |
| } | |
| } | |
| // ANCHOR New methods go here: | |
| // NOTE "supress" version where if there's an error it doesn't throw, all callback items have possible undefined | |
| // NOTE "keyed" a version where you can pass specific data to specific props sort of like cliffKeyed | |
| // NOTE Takes func object of "needs", uses them for typing inputs to callback, calls needs if need | |
| // NOTE Original implementation without a store | |
| // static profound<A extends Fox.Profound.Funcs, B>(funcs: A, callback: (c: Fox.Profound.Callback<A>) => B) { | |
| // return (input?: Fox.Profound.Param<A>) => { | |
| // const funcsKeys = Object.keys(funcs) | |
| // const valueFn = funcsKeys.reduce((acq: Fox.ChoosySpread.Func, key: string) => { | |
| // return Fox.use(acq, (acqError, acqValue) => { | |
| // if (acqError) throw acqError | |
| // // NOTE: check if key already exists, we dont need to execute fun | |
| // if (input && input[key as keyof Fox.Profound.Param<A>]) return acqValue | |
| // return Fox.use(funcs[key], (fnError, fnValue) => { | |
| // if (fnError) throw fnError | |
| // return { ...acqValue, [key]: fnValue } | |
| // })(input) | |
| // }) | |
| // }, Fox.nest(input)) | |
| // return Fox.use(valueFn, (err, value) => { | |
| // if (err) throw err | |
| // return Fox.use(callback, (err, callbackValue) => { | |
| // if (err) throw err | |
| // return callbackValue | |
| // })(value as Fox.Profound.Callback<A>) | |
| // })() | |
| // } | |
| // } | |
| // NOTE This is dumb and does not work... | |
| // static profound<A extends Fox.Profound.Funcs, B>(funcs: A, callback: (c: Fox.Profound.Callback<A>) => B) { | |
| // var store = {} | |
| // return (input?: Fox.Profound.Param<A>) => { | |
| // const funcsKeys = Object.keys(funcs) | |
| // const valueFn = funcsKeys.reduce((acq: Fox.ChoosySpread.Func, key: string) => { | |
| // return Fox.use(acq, (acqError, acqValue) => { | |
| // if (acqError) throw acqError | |
| // // NOTE: check if key already exists, we dont need to execute fun | |
| // // console.log({ key, input }) | |
| // if (input && input[key as keyof Fox.Profound.Param<A>]) return acqValue | |
| // return Fox.use(funcs[key], (fnError, fnValue) => { | |
| // if (fnError) throw fnError | |
| // return { ...acqValue, [key]: fnValue } | |
| // })({ ...input }) | |
| // }) | |
| // }, Fox.nest({ ...input, ...store })) | |
| // return Fox.use(valueFn, (err, value) => { | |
| // if (err) throw err | |
| // return Fox.use(callback, (err, callbackValue) => { | |
| // if (err) throw err | |
| // return callbackValue | |
| // })(value as Fox.Profound.Callback<A>) | |
| // })() | |
| // } | |
| // } | |
| // static example = (() => { | |
| // // NOTE Example | |
| // const dog = ({ dogName }: { dogName: string }) => `doggy ${dogName} says woof` | |
| // const cat = ({ catName }: { catName: string }) => `kitty ${catName} says meow` | |
| // const example = Fox.profound({ dog, cat }, ({ dog, cat }) => dog + cat) | |
| // const x = example({ dog: 'john', catName: 'spot' }) | |
| // })() | |
| // static exampleClass = (() => { | |
| // // NOTE Example Class | |
| // class Example { | |
| // alpha = (number: number) => number + 3 | |
| // alphaLead = Fox.lead('alpha', this.alpha) | |
| // } | |
| // const a = new Example() | |
| // const b = a.alphaLead({ alpha: 1 }) | |
| // console.log(b) | |
| // })() | |
| static tap = class { | |
| static spread<T extends Fox.Use.Func, G>(fn: T) { | |
| return <D extends Parameters<T>>(...input: D) => { | |
| return Fox.use(fn, () => { | |
| return input | |
| })(...input) as unknown as Fox.Tap.Return<T, D> | |
| } | |
| } | |
| static single<T extends Fox.Use.Func, C, D extends C, G>(fn: (a: D) => G) { | |
| return <D>(input?: D): Fox.Tap.RelatedThen<G, D> => { | |
| return Fox.use(fn, () => { | |
| return input | |
| })(input as Parameters<T>[0]) as unknown as Fox.Tap.Return<T, D> | |
| } | |
| } | |
| } | |
| // static example = (async () => { | |
| // // NOTE Example | |
| // const runZ = Fox.tap.single(console.log) | |
| // console.log(runZ('hello')) | |
| // const runA = Fox.tap.spread((val, numb) => console.log(`----- runA ${numb}`)) | |
| // console.log(runA('hello', 3)) | |
| // const runB = Fox.tap.single(() => console.log('----- runB')) | |
| // console.log(runB('meow')) // meow | |
| // const runC = Fox.tap.spread(() => console.log('----- runC')) | |
| // console.log(runC('meow')) // [meow] | |
| // const runAsyncA = Fox.tap.single(async () => console.log('----- runAsyncA')) | |
| // console.log(await runAsyncA()) | |
| // const runAsyncB = Fox.tap.single(async () => console.log('----- runAsyncB')) | |
| // console.log(await runAsyncB('meow')) | |
| // const runAsyncC = Fox.tap.spread(async () => console.log('----- runAsyncC')) | |
| // console.log(await runAsyncC('meow')) | |
| // })() | |
| // static acrew<A extends Fox.Acrew.Funcs, B extends Fox.Acrew.Callback<A>, G>(funcs: A, callback: (a: B) => G) { | |
| // return (input: Fox.Profound.Params<A>): Fox.Acrew.Return<A, G> => { | |
| // const funcsKeys = Object.keys(funcs) | |
| // const valueFn = funcsKeys.reduce((acq: Fox.ChoosySpread.Func, key: string) => { | |
| // return Fox.use(acq, (acqError, acqValue) => { | |
| // if (acqError) throw acqError | |
| // // NOTE: check if key already exists, we dont need to execute fun | |
| // if (input && input[key as keyof Fox.Profound.Param<A>]) return acqValue | |
| // return Fox.use(funcs[key], (fnError, fnValue) => { | |
| // if (fnError) throw fnError | |
| // return { ...acqValue, [key]: fnValue } | |
| // })(input) | |
| // }) | |
| // }, Fox.nest(input)) | |
| // return Fox.use(valueFn, (err, value) => { | |
| // if (err) throw err | |
| // return Fox.use(callback, (err, callbackValue) => { | |
| // if (err) throw err | |
| // return { ...value as B, ...callbackValue as {} } | |
| // })(value as B) | |
| // })() as unknown as Fox.Acrew.Return<A, G> | |
| // } | |
| // } | |
| // static acrewValue<A extends Fox.Acrew.Funcs, B extends Fox.Acrew.Callback<A>, G, F>(prein: F, funcs: A, callback: (a: B) => G) { | |
| // return (input: Fox.Profound.Param<A> = {}): Fox.Acrew.Return<A, G> & F => { | |
| // const funcsKeys = Object.keys(funcs) | |
| // const valueFn = funcsKeys.reduce((acq: Fox.ChoosySpread.Func, key: string) => { | |
| // return Fox.use(acq, (acqError, acqValue) => { | |
| // if (acqError) throw acqError | |
| // // NOTE: check if key already exists, we dont need to execute fun | |
| // if (input && input[key as keyof Fox.Profound.Param<A>]) return acqValue | |
| // return Fox.use(funcs[key], (fnError, fnValue) => { | |
| // if (fnError) throw fnError | |
| // return { ...acqValue, [key]: fnValue } | |
| // })(input) | |
| // }) | |
| // }, Fox.nest(input)) | |
| // return Fox.use(valueFn, (err, value) => { | |
| // if (err) throw err | |
| // return Fox.use(callback, (err, callbackValue) => { | |
| // if (err) throw err | |
| // return { ...value as B, ...callbackValue as {}, ...prein as {} } | |
| // })(value as B) | |
| // })() as unknown as Fox.Acrew.Return<A, G> & F | |
| // } | |
| // } | |
| // NOTE If the value of function is false, throws error | |
| static defalse<T extends Fox.Undefy.Func>(func: T, e: Error) { | |
| return (...args: Parameters<T>) => { | |
| return Fox.use(func, (error, value) => { | |
| if (error) throw error | |
| if (!value) throw e | |
| return value | |
| })(...args) as unknown as Fox.Undefy.Return<T> | |
| } | |
| } | |
| // NOTE If the value of function is true, throws error | |
| static detrue<T extends Fox.Undefy.Func>(func: T, e: Error) { | |
| return (...args: Parameters<T>) => { | |
| return Fox.use(func, (error, value) => { | |
| if (error) throw error | |
| if (value) throw e | |
| return value | |
| })(...args) as unknown as Fox.Undefy.Return<T> | |
| } | |
| } | |
| static placeholder = <T>() => { | |
| return <G extends string>(param: G) => { | |
| return (a: Record<G, T>): T => { | |
| throw new Error(`Need ${param}`) | |
| } | |
| } | |
| } | |
| static profoundRef = Symbol('Profound') | |
| static isPlainObject(obj: any): obj is ({ [name: string]: any }) { | |
| return obj && obj.constructor === Object || false; | |
| } | |
| static _pick(o: any, ...props: string[]) { | |
| return Object.assign({}, ...props.map(prop => ({ [prop]: o[prop] }))); | |
| } | |
| static profound<A extends Fox.Profound.SingleArgFuncs, G, B extends (a: Fox.Profound.Callback.ObjReturn<A>) => G, C extends Fox.Profound.Params.Value<A>>(funcs: A, callback?: B) { | |
| function reduceObjectOfFuncs(funcs: any, input: any) { | |
| if (!Fox.isPlainObject(input)) throw new Error('Needs to be plain object') | |
| const keys = Object.keys(funcs) | |
| return keys.reduce((acq: Fox.Profound.Func, key: string) => { | |
| return Fox.use(acq, (acqError, acqValue) => { | |
| if (acqError) throw acqError | |
| if (acqValue && acqValue[key]) return acqValue | |
| const isProfound = Boolean(funcs[key] && | |
| funcs[key].isProfound && | |
| funcs[key].isProfound === Fox.profoundRef && | |
| funcs[key].pass) | |
| const fn = (isProfound) ? funcs[key].pass : funcs[key] | |
| return Fox.use(fn, (fnError, fnValue) => { | |
| if (fnError) throw fnError | |
| const [value, data] = (isProfound) ? fnValue : [fnValue, {}] | |
| return { ...acqValue, ...data, [key]: value } | |
| })(acqValue) | |
| }) | |
| }, Fox.nest(input))() | |
| } | |
| // NOTE returns (callbackValue) | |
| // NOTE MARKER | |
| function profoundInner(preInput: C): Fox.Profound.Return.Value<A, B, C> { | |
| return Fox.use(reduceObjectOfFuncs, (err, input) => { | |
| if (err) throw err | |
| const keys = Object.keys(funcs) | |
| if (!callback) return Fox._pick(input, ...keys) | |
| return Fox.use(callback as Fox.Profound.Func, (err, callbackValue) => { | |
| if (err) throw err | |
| return callbackValue | |
| })(input) | |
| })(funcs, preInput) | |
| } | |
| // NOTE returns ([callbackValue, reduceObjectOfFuncsValues]) | |
| function pass(...preInput: any): any { | |
| const prein = preInput[0] || {} | |
| return Fox.use(reduceObjectOfFuncs, (err, input) => { | |
| if (err) throw err | |
| const keys = Object.keys(funcs) | |
| if (!callback) return [Fox._pick(input, ...keys), input] | |
| return Fox.use(callback as Fox.Profound.Func, (err, callbackValue) => { | |
| if (err) throw err | |
| return [callbackValue, input] | |
| })(input) | |
| })(funcs, prein) | |
| } | |
| profoundInner.isProfound = Fox.profoundRef | |
| profoundInner.pass = pass | |
| return profoundInner | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment