Typescript Proxying a function to extend accepted arguments or their types

416 Views Asked by At

I'm trying to have a class which partly acts as a Proxy wrapper around some other class, allowing the other class methods to be modified and accept either extra arguments or to change their type.

Let's imagine I have:

class MainClass {
  myMethod(txt: string) {}
  unrelatedProp = "a prop that's not a method"
}

I want to have a class that would look something like this:

class MyHelper {
  mainClass: MainClass; 
  // ... this does a bunch of other stuff not related to `MainClass`
}

const main = new MainClass();
const helper = new MyHelper(main);

helper.myMethod(2) // ← I want to extend `myMethod` to accept, for example, `string | number`.

Firstly, I'm not sure if this is even a thing and there is a better way than using Proxy to do this, but I'm doing this:

type ExtendFirstParamType<T extends (...args: any[]) => any, U>  = [
  Parameters<T>[0] | U,
  ...Parameters<T>
]
// ^^^ First problem here:
// 1. I'll be able to use this type to only extend the first parameter,
// it'd be nice to create a generic which would extend at any index
// 2. I'd need to splice/shift the parameter, instead here I'm spreading
// the rest of the parameters, so I'll get the first parameter again.

class MyHelper {
  constructor(public mainClass: MainClass) {
    const handler = {
      get(target: MainClass, propKey: keyof MainClass) {
        const property = target[propKey];

        if(typeof property !== "function") return property;
        else {
          return (...args: ExtendFirstParamType<typeof property, number>) => {
            // ^^^ Here you can see by hovering that the type is on the right track
            // it correctly infers that the first argument is now `string | number`
            return property.apply(this, args as any);
          }
        }
      }
    }

    this.mainClass = new Proxy(page, handler);
    // ^^^ How would I make sure that this type would be the proxied/extended
    // class? If I don't declare it in my class it complains that it doesn't exist
    // but if I declare it, and type it as `MainClass`, `myMethod` would not have
    // the new proxied type signature

  }
}

Here is a Typescript playground link with what I have so far.

0

There are 0 best solutions below