Decorate a class with new attributes

Typescript 4.

Using a decorator to add new methods to a class is not done seamlessly. The main problem is that decorated classes are not properly used within the type system. We thus need a factory method to create instances.

We start with an interface to declare the optional new attributes foo

interface IFoo {
  foo: string
}

The decorator adds the foo property

function decorate<
  T extends {
    new (...args: any[]): {}
  }
>(
  constructor: T
) {
  class U extends constructor {
    foo: string = 'foo'
  }
  return U
}

Then we decorate a class and define a new type to extend the class and define a factory instance maker.

@decorate
class Foo {
  bar: string = 'bar'
  static new() {
    return new Foo() as TFoo
  }
}
type TFoo = Foo & IFoo

Finally we are able to use the new decorated class. The type system won’t complain provided we use the TFoo typecast, either directly or through the static factory Foo.new

var foo = Foo.new()
console.log(foo.foo, foo.bar)
foo = new Foo() as TFoo
console.log(foo.foo, foo.bar)

Here is the Typescript playground.