Typescript编译器不知道类上的ES6代理陷阱

时间:2018-08-15 19:46:17

标签: angular typescript es6-proxy

我有一个抽象类:

abstract class Foo {
    abstract bar(): string;
}

我有一些扩展Foo的类:

class Foo1 extends Foo {
    bar(): string { return 'foo1'; }
}

class Foo2 extends Foo {
    bar(): string { return 'foo2'; }
}

我还有另一个类,我想将Foo的所有方法代理到Foo。如果我在此类上定义了Foo的所有方法,这实际上可以正常工作。但我宁愿不这样做。我希望在Foo上定义Foo的方法,并且让编译器知道FooProxy也可以实现这些方法,而不必实际实现它们。这可能吗? Proxy类看起来像这样:

class FooProxy {
    public foo: Foo;

    constructor(foo: Foo) {
        this.foo = foo;
        let handler = {
            get: function(target: FooProxy, prop: string, receiver: any) {
                if(Foo.prototype[prop] !== null) {
                    return target.foo[prop];
                }

                return Reflect.get(target, prop, receiver);
            }
        }
        return new Proxy(this, handler);
    }
}

示例:

let f = new Foo1();
let fp = new FooProxy(f);
fp.bar();

输出:

error TS2339: Property 'bar' does not exist on type 'FooProxy'.

该程序实际上在操场上运行,但是tsc不会发出任何东西。我只需要欺骗编译器如何...

2 个答案:

答案 0 :(得分:3)

在这种情况下,我认为类不是最好的方法,您只能使用函数来创建代理,并且所有方法都能按预期工作:

function createFooProxy(foo:Foo) : Foo { // Proxy<Foo> is compatible with Foo
    let handler = {
        get: function(target: Foo, prop: keyof Foo, receiver: any) {
            if(Foo.prototype[prop] !== null) {
                return foo[prop];
            }

            return Reflect.get(target, prop, receiver);
        }
    }
    return new Proxy(foo, handler);
}

如果您打算使用类方法,则可以伪造基类:

function fakeBaseClass<T>() : new() => Pick<T, keyof T>{ // we use a pick to remove the abstract modifier
    return class {} as any
}

class FooProxy extends fakeBaseClass<Foo>(){
    private foo: Foo; // I would make this private as it is not really accessible on what the constructor of FooProxy returns (maybe remove it as I see no use for it)

    constructor(foo: Foo) {
        super();
        this.foo = foo;
        let handler = {
            get: function(target: FooProxy, prop: keyof Foo, receiver: any) {
                if(Foo.prototype[prop] !== null) {
                    return target.foo[prop];
                }

                return Reflect.get(target, prop, receiver);
            }
        }
        return new Proxy(this, handler);
    }
}

答案 1 :(得分:1)

作为一个非常详尽的公认答案的补充,我想分享一个解决方案,该解决方案可用于更简单的情况,在这些情况下,您只希望能够代理任何方法或属性(处理程序中的陷阱或实际存在于实际目标上)。该解决方案基于使用“索引签名”的方式,请在此处阅读有关该主题的更多信息:https://www.typescriptlang.org/docs/handbook/interfaces.html

我希望这对其他遇到此重大问题的人有用:

const handler = {
  get: (target: object, property: string, receiver: any) => {
    // define custom traps here:
    if (property === "trap") {
      // ... do something ...
    }

    // optional test whether property exists using the Reflect class:
    if (!Reflect.has(target, property)) {
      throw Error(`Property ${property} does not exist`);
    }
    // proxy to the target using the Reflect class:
    return Reflect.get(target, property);
  },
};

我将展示两种解决方案:

1。。创建工厂和界面:

// Any property is allowed in this interface using the following index signature:
interface FooInterface {
  [key: string]: any;
}

// From the factory we return the FooInterface 
const proxyFactory = (target: Foo): FooInterface => {
  return new Proxy(target, handler);
};

2。。创建带有索引签名的类,然后直接从类构造函数中返回代理

class FooProxy {
  [key: string]: any;
  constructor(target: Foo) {
    return new Proxy(target, handler);
  }
}

可以像这样使用,在其中可以识别要为处理程序内部建立陷阱的所有可能的方法和属性:

const fooProxy = proxyFactory(foo);

const fooProxy = new FooProxy(foo);

没有更多消息抱怨:

  

类型“代理”上不存在属性“ ...属性名称... ”。

所以现在可以调用和捕获任何东西:

const returnedFromMethod = fooProxy.anyMethod();
const property = fooProxy.anyProperty;

您当然可以优化您的界面,该示例只是为了演示针对所有方法和属性的解决方案。

相关问题