如何在TypeScript中使用装饰器正确包装构造函数

the*_*rns 14 typescript

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

我有一些代码:

  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.
Run Code Online (Sandbox Code Playgroud)

我确信这是一般原型的一些细微差别,或者是TypeScript处理它们的具体方式,我没有掌握.

TSV*_*TSV 15

此代码的工作对我来说:

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

  // 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)
Run Code Online (Sandbox Code Playgroud)

  • 对我不起作用.抛出此错误:`TypeError:如果没有'new',则无法调用类构造函数Base (9认同)
  • @tmuecksch TSV 和 pablorsk 给出的代码有效,但在当前 (2018-05) jsFiddle 中无效,它给出了您报告的错误。将我们的代码粘贴到 .ts 文件中,用 tsc 编译,用 node 运行生成的 js 文件,它应该可以工作。 (2认同)
  • ES6将抛出TypeError:类构造函数Base不能在没有“ new”错误的情况下被调用。只需将`original.apply(this,args)`替换为`new original(args)`,它应该可以工作。 (2认同)

uri*_*ish 10

使用 ES2015 Proxy 覆盖构造函数的解决方案:

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 Code Online (Sandbox Code Playgroud)

你也可以在 StackBlitz 上运行它

  • 我对这种方法很好奇。这不应该是公认的答案吗,因为这看起来像是第一方支持的实现预期效果的方式?还是有陷阱? (3认同)

ete*_*ech 7

这是使用最新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)
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 尝试过这个,除非你在 Angular 中,否则它工作得很好:(在这种情况下,构造函数“args”被假定为依赖项,因此如果这不是“可注入的”,则总是“未定义”(即使确实提供了一些参数) ()`类 (2认同)