6

I wonder why when I use decorators or annotations in Typescript on a class. The compiler can't infer the new type of the class. If I don't use decorators and use the old way to do this in ES5 (ie. call manually the decorator) it obviously works.

For instance, here a sample that shows the issue:

function decorate(Target: typeof Base): IExtendedBaseConstructor {
  return class extends Target implements IExtendedBase {
    public extendedtMethod(): number {
      return 3;
    }
  };
}

interface IBase {
  baseMethod(): number;
}

interface IExtendedBase extends Base {
  extendedtMethod(): number;
}

interface IExtendedBaseConstructor {
  new(): IExtendedBase;
}

@decorate
class Base implements IBase {
  public baseMethod(): number {
    return 5;
  }
}

const test = new Base();
test.baseMethod(); // OK
test.extendedtMethod(); // NOT OK, typescript think, Base is still Base but we decorated it.

With the older way, it works:

class Base implements IBase {
  public baseMethod(): number {
    return 5;
  }
}

const ExtendedBase = decorate(Base);

const test = new ExtendedBase();
test.baseMethod(); // OK
test.extendedtMethod(); // OK

Thanks in advance.

4
  • What's the advantage of this pattern? Isn't this code a lot easier to understand? Commented Apr 3, 2016 at 19:20
  • You are right. In this case, this pattern is useless. But new front-end frameworks (like angular2) choosed to use decorator instead of inheritance to declare new components. Commented Apr 3, 2016 at 19:23
  • I have written a library that proposes to the user a helper to create a class API (with some parameters injected into it and usefull methods). So I have created an abstract Api class that provides functionalities and a decorator that registers the api into my framework via meta-data. In this case my user must do: import { AbstractAPI, Api } from 'myLib' @Api(...) class UserApi extends AbstractAPI {} My SO question is also related to this. If I could remove the extends and merge everything into my decorator @Api, it would reduce the final user typo. Commented Apr 3, 2016 at 19:31
  • oh I see. Thanks! I was curious. Commented Apr 3, 2016 at 19:38

2 Answers 2

4

Right now this doesn't work. There is a pending issue on github to allow class decorators to change the type of the class.

I would recommend doing the "old way" you mentioned until this is implemented.

Sign up to request clarification or add additional context in comments.

Comments

0

There is a way to make this work with just a little extra coding required.

For any class decorator, create an interface with its properties and methods. Name it in a way so you can easily associate it with the decorator it's describing. In your case, it could be:

interface IDecorate {
  extendedMethod(): number;
}

For any class that needs this decorator, you simply create an interface with the same name as the class and let it extend all necessary decorator interfaces:

@decorate
@anotherDecorator
class MyClass {
  // ...
}

interface MyClass extends IDecorate, IAnotherDecorator {}

Now, just turn off ESLint's or TSLint's warnings for empty interfaces and you should be ready to go. Any method or property added by the decorator will now be available to use within the decorated class itself.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.