输入' any'不是构造函数类型

时间:2016-10-09 07:35:39

标签: typescript

我正在尝试在Typescript中实现类装饰器。所以我有一个以类作为参数的函数。

const decorate = function ()
{
  return function ( target: any )
  {
    return class myExtendClass extends target{}
  };
};

目标是使用它:

@decorate()
class Something{}

不幸的是我得到了type any is not a constructor function type。知道如何实现我的目标吗?

3 个答案:

答案 0 :(得分:4)

  

我的问题是为什么这段代码不起作用,因为它的有效js

这取决于无法正常工作的含义。

生成的javascript有效且可以使用。但是如果你编写的代码不符合TS的规则,TypeScript会更加严格并且会抱怨,即使它通常会很好并且无论如何都会给你编译的js。

您尝试执行的操作的问题是TS不支持通过装饰器扩展类。它会生成有效的js,但是当你这样做时TS不了解发生了什么。

例如:

const decorate = () => (target: typeof Cat) =>
{
    return class Lion extends target
    {
        public Roar = () => console.log("Roaar")
    }
}

@decorate()
class Cat
{
    public Purr = () => console.log("Purr purr");
}

var lion = new Cat();
lion.Roar(); // Error in TypeScript but working in js

当你运行它时,这是有效的,但TS不明白你在装饰器中做了什么,所以它不知道类Cat实际上是Lion

有一个feature request让TS了解装饰器中的类突变,但此时它不受支持。

我建议你避免在像这样的装饰器中改变类。即使它可以工作,我也不会在你的例子中看到现在的观点,它不是很直观,如果你想让所有Something具有某些属性,你可以扩展直接:Something extends MyReusableProperties

答案 1 :(得分:1)

你可以尝试:

interface Newable<T> {
  new (...args: any[]): T;
}

function decorate()
{
  return function (target: Newable<Object>)
  {
    return class myExtendClass extends target {
      public someOtherProp: string;
      publicconstructor() { 
        this.someOtherProp = "test2";
      }    
    }
  };
};

但是你会得到一个新的错误:

// Error: Type 'typeof myExtendClass' is 
// not assignable to type 'typeof Something'.
@decorate() 
class Something{
  public someProp: string;
  publicconstructor() { 
    this.someProp = "test";
  }
}

您可以尝试修复它:

function decorate()
{
  return function (target: Newable<Object>)
  {
    class myExtendClass extends target {
      public someOtherProp: string;
      publicconstructor() { 
        this.someOtherProp = "test2";
      }
    }
    return (<any>myExtendClass); // notice casting!
  };
};

再次你会得到另一个错误:

let something = new Something();
console.log(something.someProp);

// Property 'someOtherProp' does not 
//exist on type 'Something'.
console.log(something.someOtherProp);

最后,您可以通过转换为任何:

来解决这个问题
console.log((<any>something).someOtherProp); // More casting!

当然,这不是一个理想的解决方案。我会尝试做类似以下的事情:

interface Newable<T> {
  new (...args: any[]): T;
}

function decorate()
{
  return function (target: Newable<Object>)
  {
    class myExtendClass extends target {
      public someOtherProp: string;
      publicconstructor() { 
        this.someOtherProp = "test2";
      }
    }
    return (<any>myExtendClass);
  };
};

function applyDecorator<T,TDecorated>(decorator, ctr): Newable<TDecorated> {
  // Decorators are just functions, you don't need @ symbol to use them :)
  let decorated: Newable<TDecorated> = <any>decorator()(ctr);
}

interface ISomething {
  someProp: string;
}

interface ISomethingElse extends Something {
  someOtherProp: string;
}

class Something implements ISomething {
  public someProp: string;
  publicconstructor() { 
    this.someProp = "test";
  }
}

let SomethingElse = applyDecorator<ISomething, ISomethingElse>(decorate, Something);

let something = new SomethingElse();
console.log(something.someProp);
console.log(something.someOtherProp);

希望有所帮助:)

答案 2 :(得分:0)

我认为问题在于装饰函数的签名。 将您的功能签名 function ( target: any ) 更改为 function <T extends { new (...args: any[]): {} }>。 为了简单起见,语句class myExtendClass extends target无错误地工作 target 必须是又名构造函数,因为它在Javascript中调用。 您不能扩展到任何,而是扩展到类(构造函数)。 这里具有约束的泛型类型T将目标约束为类(构造函数)。因此extend语句无错误地运行。  这是一个例子:

const decorate = function ()
{
    return function <T extends { new (...args: any[]): {} }>
        (target: T)
  {
      return class myExtendClass extends target{
          anotherProperty = 10; //assigning class property here
          propertyFromDecorator = "new property from decorator" // new property
    }
  };
};


@decorate()
class Something{
    myProp: string;
    anotherProperty:number;
    constructor() {
        this.myProp = "my property";
    }
} 

var someThing = new Something();
console.log(someThing);

更多信息,请参阅Typescript Decorators page on github