在TS中为长类定义动态装饰器的方法,而无需重新定义所有方法

时间:2019-01-16 13:49:16

标签: javascript typescript oop es6-class

我有一个带有很多方法的类。让我们在这里不集中讨论,可能可以将此类重构和重写为几个类,因为它将最终但不知道。我想要装饰器,它只能装饰此类的一种方法。我们可以通过几种方式做到这一点。

类界面:

interface IFoo {
    method1 (): number;
    method2 (a: string): number;
    method3 (b: IFoo): number;
    method4 (c: string | (() => string)): number;
    method5 (d: number, e: string): number;
}
  1. 经典的OOP解决方案:定义装饰器,该装饰器重新定义特定的方法,仅对所有其他方法调用超级实现。像这样的东西。

    class FooDecorator implements IFoo {
        constructor (
            private readonly provider: IFoo
        ) {}
    
        public method1 (): number {
            return this.provider.method1() + 1;
        }
    
        public method2 (a: string): number {
            return this.provider.method2.apply(this.provider, arguments);
        }
    
        public method3 (b: IFoo): number {
            return this.provider.method3.apply(this.provider, arguments);
        }
    
        public method4 (c: string | (() => string)): number {
            return this.provider.method4.apply(this.provider, arguments);
        }
    
        public method5 (d: number, e: string): number {
            return this.provider.method5.apply(this.provider, arguments);
        }
    }
    

    如您所见,相当长的编写和代码重复。

  2. 尝试利用一些JS功能。

    interface IFooDecorator {
        method1: IFoo["method1"];
    }
    
    class FooDecorator implements IFooDecorator {
        constructor (
            private readonly provider: IFoo
        ) {
            Object.setPrototypeOf(this, provider);
        }
    
        public method1 (): number {
            return this.provider.method1() + 1;
        }
    }
    

    明显的缺点是setPrototypeOf的错误键入和用法

我也尝试使用代理服务器,但是在使用代理服务器时对类型的支持也很差。还有其他解决方案吗?第一种方法是好的,以防我们可以通过.apply调用自动化未修饰方法的重新定义。

1 个答案:

答案 0 :(得分:1)

将装饰器实现为使用Object.create而不是class语法的工厂函数,这样您就无需使用Object.setPrototypeOf

function fooDecorator<T>(provider: IFoo): T implements IFoo {
    return Object.create(provider, {
        method1: {
            value(): number {
                return provider.method1() + 1;
            },
            writable: true,
            configurable: true
        }
    });
}