Typescript “固定”;类型实例化太深,可能是无限的;在合成函数中
我创建了一个函数,将函数链接在一起。我已经创建了一系列泛型类型来正确关联结果函数可能具有的参数 一切正常。。。到了一定程度。由于我生成打字的方式,当链变得太长时,TypeScript会失败。我正在寻找一种方法来缓解这个问题 我得到了这个错误:Typescript “固定”;类型实例化太深,可能是无限的;在合成函数中,typescript,infinite-loop,composition,control-flow,function-composition,Typescript,Infinite Loop,Composition,Control Flow,Function Composition,我创建了一个函数,将函数链接在一起。我已经创建了一系列泛型类型来正确关联结果函数可能具有的参数 一切正常。。。到了一定程度。由于我生成打字的方式,当链变得太长时,TypeScript会失败。我正在寻找一种方法来缓解这个问题 我得到了这个错误: 类型实例化太深,可能是无限的。 毫无疑问,这对于TypeScript来说太难处理了,而且随着每个函数的添加,类型都以指数形式映射 我意识到这段代码相当复杂,所以我不是在寻找一个完整的技术解决方案,也许是一种更符合类型脚本/算法更简单的方法来做同样的事情 n
类型实例化太深,可能是无限的。
毫无疑问,这对于TypeScript来说太难处理了,而且随着每个函数的添加,类型都以指数形式映射
我意识到这段代码相当复杂,所以我不是在寻找一个完整的技术解决方案,也许是一种更符合类型脚本/算法更简单的方法来做同样的事情
namespace Poly {
export type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }
}
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>>
}
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 FnsNoArg<T extends Funcs> = UnionToIntersectionValues<ObjEmptyNever<ObjWithoutNever<{
[K in keyof T]: Parameters<T[K]>[0] extends undefined ?
{ [KK in K]?: ThenArg<ReturnType<T[K]>> } :
never
}>>>
export type FnsHasArg<T extends Funcs> = UnionToIntersectionValues<ObjEmptyNever<ObjWithoutNever<{
[K in keyof T]: Parameters<T[K]>[0] extends undefined ?
never :
{ [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 ? Partial<FnsOverride<T>> | void : FnsHasArg<T> | FnsNoArg<T>
// export type Value<T extends Funcs> = FnArguments<T> extends never ? FnsBoth<T> | void : FnsBoth<T>
// export type Value<T extends Funcs> = FnsHasArg<T> & FnsNoArg<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> = Poly.Omit<A, keyof B>[keyof Poly.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 function isPromise(obj: any) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
}
function use<A extends Use.Func, G>(fn: A, callback: (e: Error | null, a: Use.CallbackValue<A>) => G) {
return (...args: Parameters<A>): Use.Return<A, G> => {
try {
const v = fn(...args as any[])
if (isPromise(v)) {
return v.then((v: any) => callback(null, v)).catch((e: any) => callback(e, undefined as Use.CallbackValue<A>))
}
return callback(null, v) as Use.Return<A, G>
} catch (e) {
return callback(e, undefined as Use.CallbackValue<A>) as unknown as Use.Return<A, G>
}
}
}
export function placeholder<T>() {
return <G extends string>(param: G) => {
return (a: Record<G, T>): T => {
throw new Error(`Need ${param}`)
}
}
}
const profoundRef = Symbol('Profound')
function isPlainObject(obj: any): obj is ({ [name: string]: any }) {
return obj && obj.constructor === Object || false;
}
function pick(o: any, ...props: string[]) {
return Object.assign({}, ...props.map(prop => ({ [prop]: o[prop] })));
}
function nest<T>(value: T): () => T {
return () => {
return value
}
}
export function profound<A extends Profound.SingleArgFuncs, G, B extends (a: Profound.Callback.ObjReturn<A>) => G, C extends Profound.Params.Value<A>>(funcs: A, callback?: B) {
function reduceObjectOfFuncs(funcs: any, input: any = {}) {
if (!isPlainObject(input)) throw new Error('Needs to be plain object')
// console.log(input)
const keys = Object.keys(funcs)
return keys.reduce((acq: Profound.Func, key: string) => {
return use(acq, (acqError, acqValue) => {
if (acqError) throw acqError
if (acqValue && acqValue.hasOwnProperty(key)) return acqValue
const isProfound = Boolean(funcs[key] &&
funcs[key].isProfound &&
funcs[key].isProfound === profoundRef &&
funcs[key].pass)
const fn = (isProfound) ? funcs[key].pass : funcs[key]
return use(fn, (fnError, fnValue) => {
if (fnError) throw fnError
const [value, data] = (isProfound) ? fnValue : [fnValue, {}]
return { ...acqValue, ...data, [key]: value }
})(acqValue)
})
}, nest(input))()
}
// NOTE returns (callbackValue)
// NOTE MARKER
function profound(preInput: C): Profound.Return.Value<A, B, C> {
return use(reduceObjectOfFuncs, (err, input) => {
if (err) throw err
const keys = Object.keys(funcs)
if (!callback) return pick(input, ...keys)
return use(callback as Profound.Func, (err, callbackValue) => {
if (err) throw err
return callbackValue
})(input)
})(funcs, preInput)
}
// NOTE returns ([callbackValue, reduceObjectOfFuncsValues])
function pass(preInput: any): any {
return use(reduceObjectOfFuncs, (err, input) => {
if (err) throw err
const keys = Object.keys(funcs)
if (!callback) return [pick(input, ...keys), input]
return use(callback as Profound.Func, (err, callbackValue) => {
if (err) throw err
return [callbackValue, input]
})(input)
})(funcs, preInput)
}
profound.isProfound = profoundRef
profound.pass = pass
return profound
}
import { placeholder, profound } from '@reggi/profound'
const alpha = profound({}, ({ }) => 'hello world I\'m a profound')
// console.log(alpha()) // hello world I\'m a profound
const beta = profound({ alpha }, ({ alpha }) => `I am using alpha as a dependency (${alpha})`)
// console.log(beta()) // I am using alpha as a dependency (hello world I'm a profound)
const gamma = profound({ alpha, beta }, ({ alpha, beta }) => `
Gamma needs alpha and beta to run.
Rather than being very redundant and running both in here. They are fetched for use.
If you pass alpha or beta into gamma, they are not run.
This function also takes any arguments that alpha, or beta need.
Profounds become async:
1. If their dependencies are async and are being run (input dependent)
2. If this callback is async
(${alpha} ${beta})
`)
const age = placeholder<number>()('age')
const delta = profound({ age, gamma }, ({ age, gamma }) => gamma.length + age)
console.log(delta({ age: 30 })) // 514
import {profound} from './profound';
export const example0 = profound({}, () => 'anything');
export const example1 = profound({example0}, () => 'anything');
export const example2 = profound({example1}, () => 'anything');
export const example3 = profound({example2}, () => 'anything');
export const example4 = profound({example3}, () => 'anything');
export const example5 = profound({example4}, () => 'anything');
export const example6 = profound({example5}, () => 'anything');
export const example7 = profound({example6}, () => 'anything');
export const example8 = profound({example7}, () => 'anything');
export const example9 = profound({example8}, () => 'anything');
export const example10 = profound({example9}, () => 'anything');
export const example11 = profound({example10}, () => 'anything');
export const example12 = profound({example11}, () => 'anything');
export const example13 = profound({example12}, () => 'anything');
export const example14 = profound({example13}, () => 'anything');
export const example15 = profound({example14}, () => 'anything');
export const example16 = profound({example12, example13, example14, example15}, () => 'anything');
export const example17 = profound({example16, example13, example14, example15}, () => 'anything');