title: Class decorator with generic parameter
body: <p>I want to write a class decorator that takes a parameter. The trouble is that I want to enforce the type of the parameter to be related to the class itself.
  </p>
  
  <pre class="lang-js prettyprint-override"><code>class Options&lt;T&gt; {
    accept: (instance: T)=&gt;boolean;
  }
  
  var MyOptions: Options&lt;MyClass&gt;={
    accept: function (instance: MyClass)
              { return true; }
  };
  
  @deco(MyOptions)
  class MyClass {
    y: string;
    constructor() {
      this.y="Hello";
    }
  }
  </code></pre>
  
  <p>Another goal is that I need to subclass the constructor, and give it access to the options, so the whole implementation must be written inside <code>deco</code>. This is as far as I've gotten, through trial and plenty of error:</p>
  
  <pre class="lang-js prettyprint-override"><code>function imported&lt;T&gt;(instance: T, options: Options&lt;T&gt;): any { return {}; }
  
  function deco&lt;T extends {}&gt;(options: Options&lt;T&gt;) {
    return function&lt;CtorOfT extends {new(...args:any[]):T}&gt;(ctr: CtorOfT) {
      let options_copy: Options&lt;sub&gt;=options;
  
      //replace constructor to add $accepted and $imported
      class sub extends ctr {
        $accepted: boolean;
        $imported: any;
        constructor(...args:any[]) {
          super(args);
          this.$accepted = options_copy.accept(this);
          this.$imported = imported&lt;sub&gt;(this, options_copy);
        }
      };
      return sub;
    }
  }
  </code></pre>
  
  <p>The compiler complains about line</p>
  
  <pre class="lang-js prettyprint-override"><code>class sub extends ctr {
  </code></pre>
  
  <p>that
  </p>
  
  <pre class="lang-js prettyprint-override"><code>Base constructor return type 'T' is not a class or interface type.
  </code></pre>
  
  <p>I tried to fix that with <code>&lt;T extends {}&gt;</code> but it didn't quite help. It compiles when I replace</p>
  
  <pre class="lang-js prettyprint-override"><code>new(...args:any[]):T
  </code></pre>
  
  <p>with</p>
  
  <pre class="lang-js prettyprint-override"><code>new(...args:any[]):{}
  </code></pre>
  
  <p>which is what I'm doing, but it loses the type connection between <code>MyOptions</code> and <code>MyClass</code>. As you can easily guess, I have no idea what I'm doing (wrong).</p>
  
  <p>Link to <a href="http://www.typescriptlang.org/play/index.html#src=class%20Options%3CT%3E%20%7B%0D%0A%20%20accept%3A%20(instance%3A%20T)%20%3D%3E%20boolean%3B%0D%0A%7D%0D%0A%0D%0Aclass%20Foo%20%7B%0D%0A%20%20x%3A%20number%3B%0D%0A%7D%0D%0A%0D%0Avar%20MyOptions%3A%20Options%3CMyClass%3E%3D%7B%0D%0A%20%20accept%3A%20function%20(instance%3A%20MyClass)%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20return%20true%3B%20%7D%0D%0A%7D%3B%0D%0A%0D%0Avar%20FooOptions%3A%20Options%3CFoo%3E%3D%7B%0D%0A%20%20accept%3A%20function%20(instance%3A%20Foo)%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20return%20true%3B%20%7D%0D%0A%7D%3B%0D%0A%0D%0A%40deco(MyOptions)%0D%0Aclass%20MyClass%20%7B%0D%0A%20%20y%3A%20string%3B%0D%0A%20%20constructor()%20%7B%0D%0A%20%20%20%20this.y%3D%22Hello%22%3B%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Afunction%20testIsFunction(f%3A%20Function)%3A%20void%20%7B%20%7D%0D%0Afunction%20imported%3CT%3E(instance%3A%20T%2C%20options%3A%20Options%3CT%3E)%3A%20any%20%7B%20return%20%7B%7D%3B%20%7D%0D%0A%0D%0Afunction%20deco%3CT%20extends%20%7B%7D%3E(options%3A%20Options%3CT%3E)%20%7B%0D%0A%20%20return%20function%3CCtorOfT%20extends%20%7Bnew(...args%3Aany%5B%5D)%3AT%7D%3E(ctr%3A%20CtorOfT)%20%7B%0D%0A%20%20%20%20let%20options_copy%3A%20Options%3Csub%3E%3Doptions%3B%0D%0A%20%20%20%20testIsFunction(ctr)%3B%0D%0A%0D%0A%20%20%20%20%2F%2Freplace%20constructor%20to%20add%20%24accepted%20and%20%24imported%0D%0A%20%20%20%20class%20sub%20extends%20ctr%20%7B%0D%0A%20%20%20%20%20%20%24accepted%3A%20boolean%3B%0D%0A%20%20%20%20%20%20%24imported%3A%20any%3B%0D%0A%20%20%20%20%20%20constructor(...args%3Aany%5B%5D)%20%7B%0D%0A%20%20%20%20%20%20%20%20super(args)%3B%0D%0A%20%20%20%20%20%20%20%20this.%24accepted%20%3D%20options_copy.accept(this)%3B%0D%0A%20%20%20%20%20%20%20%20this.%24imported%20%3D%20imported(this%2C%20options_copy)%3B%0D%0A%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%7D%3B%0D%0A%20%20%20%20return%20sub%3B%0D%0A%20%20%7D%0D%0A%7D%0D%0A" rel="nofollow noreferrer">Repl</a> tested with TSC 2.8.1  (<a href="http://www.typescriptlang.org/play/index.html#src=class%20Options%3CT%3E%20%7B%0D%0A%20%20accept%3A%20%3CT%3E(instance%3A%20T)%20%3D%3E%20boolean%3B%0D%0A%7D%0D%0A%0D%0Avar%20MyOptions%3A%20Options%3CMyClass%3E%3D%7B%0D%0A%20%20accept%3A%20function%20%3CMyClass%3E(instance%3A%20MyClass)%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20return%20true%3B%20%7D%0D%0A%7D%3B%0D%0A%0D%0A%40deco(MyOptions)%0D%0Aclass%20MyClass%20%7B%0D%0A%20%20y%3A%20string%3B%0D%0A%20%20constructor()%20%7B%0D%0A%20%20%20%20this.y%3D%22Hello%22%3B%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Afunction%20testIsFunction(f%3A%20Function)%3A%20void%20%7B%20%7D%0D%0Afunction%20imported%3CT%3E(instance%3A%20T%2C%20options%3A%20Options%3CT%3E)%3A%20any%20%7B%20return%20%7B%7D%3B%20%7D%0D%0A%0D%0Afunction%20deco%3CT%20extends%20%7B%7D%3E(options%3A%20Options%3CT%3E)%20%7B%0D%0A%20%20return%20function%3CCtorOfT%20extends%20%7Bnew(...args%3Aany%5B%5D)%3AT%7D%3E(ctr%3A%20CtorOfT)%20%7B%0D%0A%20%20%20%20let%20options_copy%3A%20Options%3Csub%3E%3Doptions%3B%0D%0A%20%20%20%20testIsFunction(ctr)%3B%0D%0A%0D%0A%20%20%20%20%2F%2Freplace%20constructor%20to%20add%20%24accepted%20and%20%24imported%0D%0A%20%20%20%20class%20sub%20extends%20ctr%20%7B%0D%0A%20%20%20%20%20%20%24accepted%3A%20boolean%3B%0D%0A%20%20%20%20%20%20%24imported%3A%20any%3B%0D%0A%20%20%20%20%20%20constructor(...args%3Aany%5B%5D)%20%7B%0D%0A%20%20%20%20%20%20%20%20super(args)%3B%0D%0A%20%20%20%20%20%20%20%20this.%24accepted%20%3D%20options_copy.accept(this)%3B%0D%0A%20%20%20%20%20%20%20%20this.%24imported%20%3D%20imported(this%2C%20options_copy)%3B%0D%0A%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%7D%3B%0D%0A%20%20%20%20return%20sub%3B%0D%0A%20%20%7D%0D%0A%7D%0D%0A" rel="nofollow noreferrer">previous version</a>)</p>
  
  <p><strong>Edit</strong>: By type connection, I mean that the following should be an error:</p>
  
  <pre class="lang-js prettyprint-override"><code>class Foo {
    x: number;
  }
  
  var FooOptions: Options&lt;Foo&gt;={
    accept: function (instance: Foo)
              { return true; }
  };
  
  @deco(FooOptions) //error, Foo vs MyClass
  class MyClass {
    ...
  </code></pre>
  
  <p>Thanks to jcalz, I've noticed I had Options.accept declared as a generic when I shouldn't have. Edited that out, but now it complains about</p>
  
  <pre class="lang-js prettyprint-override"><code>let options_copy: Options&lt;sub&gt;=options;
  </code></pre>
  
  <p>as well. I'll try to understand the discussion of <a href="https://github.com/Microsoft/TypeScript/issues/16390" rel="nofollow noreferrer">Github #16390 issue</a> but it's really convoluted.</p>