如何在TypeScript

时间:2015-12-22 08:35:54

标签: typescript

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

我有一些代码:

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

    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处理它们的具体方式,我不理解。

5 个答案:

答案 0 :(得分:13)

这段代码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)
  }

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

答案 1 :(得分:3)

其他答案中的评论抱怨代码不起作用 实际上,它有效,但不适用于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();

[编辑]还添加了一行复制静态成员,否则在调用静态方法时,装饰类会抛出错误。

答案 2 :(得分:3)

这是使用最新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

答案 3 :(得分:2)

如果您喜欢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
*/

答案 4 :(得分:1)

使用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