2

I'm currently trying to create a decorator to mix new methods into a React component. I've gotten the desired result via the following:

function testDecorator(target) {
  const testMethod = function testMethod() {
    return 'successfully mixed testMethod into component';
  };
  target.prototype.testMethod = testMethod;
}

@testDecorator
export class TestComponent extends React.Component<{}, {}> {
// ...
  render() {
    console.log('this.testMethod', this.testMethod());
    // outputs "successfully mixed testMethod into component"
  }
}

This compiles and outputs the expected value, but produces a transpiler error: Property 'testMethod' does not exist on type 'TestComponent'. Is there a way to avoid the error? I.E. is it possible to teach Typescript about values mixed into a (React component) class via a decorator?

Andrew Faulkner
  • 3,662
  • 3
  • 21
  • 24
  • 1
    Maybe using [mixins](https://www.typescriptlang.org/docs/handbook/mixins.html) is a better approach here? – Nitzan Tomer Aug 29 '16 at 11:57
  • In the end that was pretty similar to what I ended up doing. However, I explicitly wanted to avoid mixins, since they're considered harmful by React's creators. That said, 'mixin decorators' likely have all the same issues as standard mixins, so it probably doesn't matter too much (Nonetheless, it's still useful since React.createClass has now been deprecated - mixins will soon be off the table as an available solution). – Andrew Faulkner Jun 27 '17 at 20:18

3 Answers3

1

Right now there is no way to omit the error except of removing type checks with casting to any or interface that does have this method defined.

Here is pending request: Class Decorator Mutation

Amid
  • 21,508
  • 5
  • 57
  • 54
1

Thanks to @Amid's link to the pending Class Decorator Mutation Typescript request I discovered there's no way to do this without additional boilerplate. I thus settled for a workaround, in which I:

  1. have any class the decorator wraps also implement an interface providing types for all properties the decorator adds; and
  2. provide stand-ins for the added functions in the class itself, to satisfy the compiler.

For example:

const testDecorator = (target: Function) => {
  const testMethod = () => 'successfully mixed testMethod into component';
  target.prototype.testMethod = testMethod;
};

interface TestDecorated {
  testMethod: () => any;
}

@testDecorator
export class TestClass extends React.Component<{}, {}> implements TestDecorated {
  //...       
  testMethod;

  render() {
    console.log('this.testMethod', this.testMethod());
    // outputs: "successfully mixed testMethod into component"
  }
}
Andrew Faulkner
  • 3,662
  • 3
  • 21
  • 24
-1

Maybe you can use these lib?! Or at least can take the idea how to implement the decorator:

core decorators

Inoir
  • 96
  • 7
  • The core issue here is getting it to work with Typescript, so that repo isn't really what I'm looking for (since it contains only Javascript). – Andrew Faulkner Aug 29 '16 at 11:27
  • Hmm okay maybe these guys here can help you? http://stackoverflow.com/questions/29775830/how-to-implement-a-typescript-decorator?rq=1 – Inoir Aug 29 '16 at 11:28
  • I'd already looked through those answers, and they weren't able to help either: none of the class decorator examples in there mutate the class they're wrapping, which is the crux of my problem. – Andrew Faulkner Aug 29 '16 at 11:39