type AppendDotPathSegment< TDotPath extends string, TSegment extends string, > = TDotPath extends "" ? TSegment : `${TDotPath}.${TSegment}`; // eslint-disable-next-line @typescript-eslint/no-explicit-any type AnyFunction = (...args: any[]) => any; type Primitives = | null | undefined | string | number | boolean | symbol | bigint | AnyFunction; type AllObjDotPaths = TObj extends | Primitives | unknown[] ? TDotPath : { [P in keyof TObj]: P extends string ? | AppendDotPathSegment | AllObjDotPaths> : TDotPath; }[keyof TObj]; type RecursiveOmitNested< TObj, TOmitPath extends string, TDotPath extends string = "", > = TObj extends Primitives | unknown[] ? TObj : { [P in keyof TObj as P extends string ? AppendDotPathSegment extends TOmitPath ? never : P : never]: P extends string ? RecursiveOmitNested< TObj[P], TOmitPath, AppendDotPathSegment > : TObj[P]; }; type OnlyProcedures = TProceduresInstance extends AnyFunction ? TProceduresInstance : { [P in keyof TProceduresInstance as OnlyProcedures< TProceduresInstance[P] > extends Exclude | Record ? never : P]: OnlyProcedures; }; type _ProceduresFromInstanceConfig = { omit?: readonly string[]; }; const _proceduresFromInstance = ( proceduresInstance: TProceduresInstance, config: _ProceduresFromInstanceConfig = {}, path: string[] = [], ): OnlyProcedures => { const omit: readonly string[] = config.omit ?? []; const res = {} as OnlyProcedures; if (proceduresInstance) { const proto = Object.getPrototypeOf(proceduresInstance); const properties = [ ...Object.getOwnPropertyNames(proceduresInstance), ...(proto ? Object.getOwnPropertyNames(proto) : []), ]; for (const key of properties) { const value = proceduresInstance[key as keyof typeof proceduresInstance]; if (key === "constructor" || omit.includes([...path, key].join("."))) { continue; } if (typeof value === "function") { res[key as keyof typeof res] = value.bind(proceduresInstance); } else if (typeof value === "object") { res[key as keyof typeof res] = _proceduresFromInstance(value, config, [ ...path, key, ]) as (typeof res)[keyof typeof res]; } } } return res; }; export type OmittableProcedures = AllObjDotPaths< OnlyProcedures >; export type ProceduresFromInstance< TProceduresInstance, TOmitPaths extends string = never, > = RecursiveOmitNested, TOmitPaths>; type ProceduresFromInstanceConfig< TProceduresInstance, TUnknownOmitPaths extends string, > = { omit?: readonly ( | TUnknownOmitPaths | OmittableProcedures )[]; }; export const proceduresFromInstance = < TProceduresInstance, TUnknownOmitPaths extends string = never, >( proceduresInstance: TProceduresInstance, config: ProceduresFromInstanceConfig< TProceduresInstance, TUnknownOmitPaths > = {}, ): ProceduresFromInstance => { return _proceduresFromInstance( proceduresInstance, config, ) as ProceduresFromInstance; };