Typescript: function should return proxy object of generic object type

456 Views Asked by At

The following function should create a proxy object with a specific handler, but typescript is throwing a type error when I try it that way.

function createProxiedObject<T extends object>(obj: T): T {

  const handler = {
    set(obj: {[key: string]: any}, prop: string, value: any) {
      console.log(`changed ${prop} from ${obj[prop]} to ${value}`);
      obj[prop] = value;
      return true;
    }
  };

  return new Proxy(obj, handler)
}

Error:

Type '{ [key: string]: any; }' is not assignable to type 'T'.
  '{ [key: string]: any; }' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'object'.(2322)

It does not make a difference when I check if obj is of type object.

I can't see why this shouldn't work. It matches with the ProxyConstructor interface:

interface ProxyConstructor {
  // ...
  new <T extends object>(target: T, handler: ProxyHandler<T>): T;
}

What am I doing wrong here?

1

There are 1 best solutions below

0
On BEST ANSWER

If we take a look at Proxy's type definition we will see that it accepts a generic parameter:

interface ProxyConstructor {
    /**
     * Creates a Proxy object. The Proxy object allows you to create an object that can be used in place of the
     * original object, but which may redefine fundamental Object operations like getting, setting, and defining
     * properties. Proxy objects are commonly used to log property accesses, validate, format, or sanitize inputs.
     * @param target A target object to wrap with Proxy.
     * @param handler An object whose properties define the behavior of Proxy when an operation is attempted on it.
     */
    new <T extends object>(target: T, handler: ProxyHandler<T>): T;
}
declare var Proxy: ProxyConstructor;

You can manually pass the correct type, instead of letting the typescript do it automatically, thus solutions looks like this:

function createProxiedObject<T extends object>(obj: T): T {
  const handler = {
    set(obj: { [key: string]: any }, prop: string, value: any) {
      console.log(`changed ${prop} from ${obj[prop]} to ${value}`);
      obj[prop] = value;
      return true;
    },
  };

  return new Proxy<T>(obj, handler);
}

playground