【问题标题】:Typescript Compiler Does Not Know About ES6 Proxy Trap on ClassTypescript 编译器不知道类上的 ES6 代理陷阱
【发布时间】:2019-01-22 17:28:21
【问题描述】:

我有一个抽象类:

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 不发出任何东西。我只需要欺骗编译器一些方法......

【问题讨论】:

    标签: angular typescript es6-proxy


    【解决方案1】:

    我不认为在这种情况下类是最好的方法,你可以使用一个函数来创建代理,一切都会按预期工作:

    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);
        }
    }
    

    【讨论】:

    • 您的回答有效,我感谢您。我最终使用了假基类,因为在 FooProxy 是 Angular 服务和 createFooProxy 是服务提供者的情况下,我无法让函数正常工作。在这种情况下,我想返回new Proxy(this, handler) 而不是new Proxy(foo, handler),因为我需要FooProxy 来保留它自己的属性和方法。抛出的错误是FooProxy 没有与“Foo”类型相同的属性。依赖注入也使问题有些混乱。如果您对此有任何额外的智慧,那就太好了!
    • 我很好奇为什么Foo.prototype[prop] 永远为空(而不是未定义或其他一些值)。在什么情况下它归结为Reflect.get 方法?我也猜到了相反的问题:你能做到Reflect.get 并削减原型方法吗?
    【解决方案2】:

    作为已接受答案的补充,我想分享一个解决方案,该解决方案可用于更简单的情况,您只是希望能够代理任何方法或属性(处理程序中的陷阱或实际存在的东西)在实际目标上)。该解决方案基于使用“索引签名”,在此处阅读有关该主题的更多信息: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;
    

    你当然可以优化你的界面,这个例子只是为了演示所有方法和属性的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-28
      相关资源
      最近更新 更多