Karol Janyst
Karol Janyst

Reputation: 356

Class decorator signature with required method

I want to define a decorator signature for class decorator in typescript for a javascript code that already exists. Let's say I have some already defined class Foo

class Foo {
  someMethod() {
  }
}

Then there is also some class decorator method which take input class but it requires that the input class has some other method defined before it is decorated.

function decorate(someClass) {
  // some properties and methods injection happens here
}

Then in my typescript code I would create a class to decorate as:

class Bar extends Foo {
  requiredMethod(): any {
    return null;
  }
}

Then for definitions I tried:

interface WithRequiredMethod {
  requiredMethod(): any;
}

declare function decorate<C extends Foo>(input: C & WithRequiredMethod): C;

And then call it as:

const DocoratedBar = decorate(Bar);

Unfortunately typescript doesn't recognise Bar as valid input even if it has requiredMethod defined. Is there any way to define decorate signature to validate presence of requiredMethod on input class?

Upvotes: 0

Views: 590

Answers (1)

Aluan Haddad
Aluan Haddad

Reputation: 31823

You almost have it. But you need to write this

interface WithRequiredMethod {
  // tslint:disable-next-line:no-any
  requiredMethod(): any;
}

declare function decorate<C extends new () => Foo & WithRequiredMethod>(target: C): C;

The reason is that the decorator targets a class. A class is really just a constructor function. So the object that gets decorated is actually a constructor function for objects of type Foo & WithRequiredMethod.

For example, if we only wanted to restrict our decorator to targeting Foo or one of its subclasses we would write

declare function decorate<T extends typeof Foo>(target: T): T;

Interestingly, the error originally received is stating that class Bar does not implement WithRequiredMethod, which is indeed true, but not what we wanted to say. But, it could well be useful to say and when so we can leverage decorators to enforce both static and instance properties.

Consider:

declare function decorate<
  C extends WithRequiredMethod & (new () => Foo & WithRequiredMethod)
>(target: C): C;

Now we apply this to class Bar and again receive the error that requiredMethod is missing. We can however satisfy the requirement by adding a staticrequiredMethod method to Bar:

@decorate class Bar extends Foo {
  // required by `decorate`
  static requiredMethod() {
    // tslint:disable-next-line:no-null-keyword
    return null;
  }

  // also required by `decorate`
  requiredMethod() {
    // tslint:disable-next-line:no-null-keyword
    return null;
  }
}

Upvotes: 1

Related Questions