Utility for Wrapping and Proxying Async and Sync Functions in Non-Effect Libraries

In the real world we have to interact with non effect libraries, and often need to wrap classes/objects function into Effect.tryPromise, Effect.try etc.
It would be nice to have some utilities that could easily wrap an object and simply proxy the async functions, and sync functions, and have a general error be thrown when these fail:

Is this a good idea? of course i know that a specific wrapper is better, but it's a lot of work

type AsyncMethod<T, E> = T extends (...args: infer A) => Promise<infer R>
  ? (...args: A) => Effect.Effect<R, E, never>
  : T;

type WrapAsyncMethods<T, E> = {
  [K in keyof T]: AsyncMethod<T[K], E>;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const asyncMethodsProxyHandler: ProxyHandler<any> = {
  get(target, property) {
    const originalMethod = target[property];
    if (typeof originalMethod === 'function' && originalMethod.constructor.name === 'AsyncFunction') {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      return (...args: any[]) =>
        Effect.tryPromise({
          try: async () => await originalMethod.apply(this, args),
          catch: (e) => new Error(`${originalMethod.name} failed: ${String(e)}`),
        });
    }
    return originalMethod;
  },
};

export const wrapInEffect = <T>(x: T) => new Proxy(x, asyncMethodsProxyHandler) as WrapAsyncMethods<T>, Error>;


I can image that we could expand the WrapAsyncMethods and asyncMethodsProxyHandler to have options to define how to do it, e.g.:

{
  functionA: 'asyncWithTimeout',
  functionB: 'async'
}
Was this page helpful?