如何在TypeScript中用修饰符正确包装构造函数

问题描述 投票:17回答:5

用装饰器包装类的过程会导致超类无法访问该类的属性。为什么?

我有一些代码:

  1. 创建一个装饰器,该装饰器用应该做完全相同的事情的新构造函数替换类的构造函数。
  2. 使用属性创建基类。
  3. 用包装装饰器包装基类。
  4. 创建一个扩展基类的类。
  5. 尝试访问扩展类上的属性。这是失败的部分。

这里是代码:

function wrap(target: any) {
  // the new constructor
  var f: any = function (...args) {
      return new target();
  }

  f.prototype = target.prototype;
  return f;
}

@wrap
class Base {
    prop: number = 5;
}

class Extended extends Base {
    constructor() {
        super()
    }
}

var a = new Extended()
console.log(new Extended().prop) // I'm expecting 5 here, but I get undefined.

我确信这是一般原型的某种细微差别,或者是TypeScript处理我不掌握的特定方式。

typescript
5个回答
15
投票

此代码对我来说是works

function logClass(target: any) {
  // save a reference to the original constructor
  var original = target;

  // the new constructor behaviour
  var f : any = function (...args) {
    console.log("New: " + original.name); 
    //return  original.apply(this, args);
    return new original(...args); // according the comments
  }

  // copy prototype so intanceof operator still works
  f.prototype = original.prototype;

  // return new constructor (will override original)
  return f;
}

@logClass
class Base {
    prop: number = 5;
}

class Extended extends Base {
    constructor() {
        super()
    }
}

var b = new Base()
console.log(b.prop)

var a = new Extended()
console.log(a.prop)

14
投票

这是使用最新的TS(3.2.4)的更现代的方法。下面还使用了装饰器工厂模式,因此您可以传递属性:

function DecoratorName(attr: any) {
  return function _DecoratorName<T extends {new(...args: any[]): {}}>(constr: T){
    return class extends constr {
      constructor(...args: any[]) {
        super(...args)
        console.log('Did something after the original constructor!')
        console.log('Here is my attribute!', attr.attrName)
      }
    }
  }
}

请参见此处以获取更多信息:https://www.typescriptlang.org/docs/handbook/decorators.html#class-decorators


5
投票

使用ES2015代理重写构造函数的解决方案:

function wrap(target: any) {
  return new Proxy(target, {
    construct(clz, args) {
      console.log(`Constructing ${target.name}`);
      return Reflect.construct(clz, args);
    }
  });
}

@wrap
class Base {
  prop: number = 5;
}

class Extended extends Base {
  constructor() {
    super()
  }
}

var a = new Extended()
console.log(new Extended().prop);

您也可以run this on StackBlitz


2
投票

其他答案中的注释抱怨代码不起作用。实际上,它可以工作,但不能在jsFiddle中使用...这是jsFiddle中代码生成的问题(可能使用过时的TypeScript版本)。上面的代码可用于TypeScript 2.7.2(与Node一起运行)。

因此,这基本上是pablorsk答案中的代码(除非不需要返回实例,我只是添加了完整类型以取更严格的TSLint ...

function logClass<T extends { new(...args: any[]): {} }>(): any {
    type Ctor = new (...args: any[]) => T;
    return (target: T): Ctor => {
        // Save a reference to the original constructor
        const Original = target;

        // the new constructor behaviour
        let decoratedConstructor: any = function (...args: any[]): void {
            console.log("Before construction:", Original);
            Original.apply(this, args);
            console.log("After construction");
        };

        // Copy prototype so intanceof operator still works
        decoratedConstructor.prototype = Original.prototype;
        // Copy static members too
        Object.keys(Original).forEach((name: string) => { decoratedConstructor[name] = (<any>Original)[name]; });

        // Return new constructor (will override original)
        return decoratedConstructor;
    };
}

@logClass()
class Base {
    prop = 5;
    constructor(value: number) {
        console.log("Base constructor", value);
        this.prop *= value;
    }
    foo() { console.log("Foo", this.prop); }
    static s() { console.log("Static s"); }
}

class Extended extends Base {
    constructor(init: number) {
        super(init);
        console.log("Extended constructor", init);
    }
    bar() { console.log("Bar", this.prop); }
}

const b = new Base(2);
console.log("Base", b instanceof Base);
b.foo();
Base.s();

const e = new Extended(5);
console.log("Extended", e instanceof Base, e instanceof Extended);
e.bar();

[[EDIT]还添加了一行复制静态成员的行,否则修饰的类在调用静态方法时会引发错误。


1
投票

如果您喜欢run code after and before constructor() with a decorator

function ClassWrapper() {
    return function(target: any) {
        // save a reference to the original constructor
        var original = target;

        // the new constructor behaviour
        var f: any = function (...args) {
            console.log('ClassWrapper: before class constructor', original.name);
            let instance = original.apply(this, args)
            console.log('ClassWrapper: after class constructor', original.name);
            return instance;
        }

        // copy prototype so intanceof operator still works
        f.prototype = original.prototype;

        // return new constructor (will override original)
        return f;
    };
}
@ClassWrapper()
export class ClassExample {
    public constructor() {
        console.info('Running ClassExample constructor...');
    }
}

let example = new ClassExample();

/*
CONSOLE OUTPUT:
ClassWrapper: before class constructor ClassExample
Running ClassExample constructor...
ClassWrapper: after class constructor ClassExample
*/
© www.soinside.com 2019 - 2024. All rights reserved.