3

In ES6 classes, I find myself creating static create methods, prefering to do ClassName.create() rather than new ClassName() or new ClassName, since I have run into some silly errors having to do with accessing properties on the newly created object; slipping up and doing new ClassName.methodCall() instead of (new ClassName).methodCall(). IMO it's cleaner to do ClassName.create().methodCall() and less prone to silly and difficult-to-catch errors.

However, it gets tedious and verbose to do this on every class, so I was wondering if there was a way to use TS/ES6 decorators so that I could do

@Creatable // or @Creatable()
class ClassName {
    constructor(arguments: IArguments) {}
}

which would create a static create(arguments: IArguments) { return new this(arguments); } method on the class, with the same arguments, including types.

Is this possible, and if so how?

5
  • extends Creatable; class Creatable { create(args...){ var obj=Object.create(this); this.constructor.call(this,...args);return obj;}} Commented May 21, 2017 at 16:16
  • forgot to mention I would like to have it type safe, will edit the question Commented May 21, 2017 at 16:17
  • Actually new ClassName(…).methodCall() works just fine. It's only when you omit the constructor argument list (new ClassName.methodCall()) that you need to group it into (new ClassName).methodCall(). Commented May 21, 2017 at 16:17
  • @Jonasw Don't use inheritance for this (also your method doesn't do what the OP wants) Commented May 21, 2017 at 16:17
  • Seems you're right about that, where I had issues there were no arguments. I will update the question, because regardless I would like to use the .create() pattern Commented May 21, 2017 at 16:21

1 Answer 1

1

It's currently not possible to easily add new methods to classes with decorators in TypeScript, there's open issue for that.

Doing that will require to use type assertions, something like:

interface ICreatable<T> extends Function {
  new (): T;
  create: (...args) => T;
}

function Creatable() {
  return function (target) {
    target.create = (...args) => new target(...args);
    return target;
  }
}

@Creatable()
class Foo {...}

(<ICreatable<Foo>>Foo).create(...)

Considering the problem statement, this is XY problem. While omitted constructor parentheses are the real problem.

The trouble-free way to instantiate classes with and without arguments is

new Foo().bar();
new Foo(1).bar();

And not

(new Foo).bar();

Optional constructor parentheses are often considered bad manners and banned in style guides. If a habit causes problems, it can be considered bad habit and should be avoided. Just don't do that. There's no real need for create factory method here.

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

2 Comments

Will take your advice on just using parens after the class name. I will clarify the question though, because I'm still interested; it doesn't really matter to me if it's @Creatable or @Creatable()
The first part of the answer addresses that. It's impractical to do that with decorators in TS, as you can see.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.