How to extend class type with decorator

Viewed 253

I'd like to use decorators in my project. Here's what I have written:

export const DetachedWindow = (options: TDetachedWindowOptions = defaultOptions) => <R extends Constructable>(Clazz: R): R => {
    const tmp = class extends Clazz {
        value = 'value';

        value2 = 'value2';

        constructor(...args: any[]) {
            super(...args);
            // <some logick>
        }
    };
    Object.defineProperty(tmp, 'name', { value: Clazz.name });
    return tmp;
};

As you see this decorators creates a few fields. But typescript can't recognize them

@DetachedWindow({ optA: 'optA' })
export class SomeClass {
    constructor() {
        setTimeout(() => {
            console.log(this.value); // TS2339: Property 'value' does not exist on type 'SomeClass'.
        });
    }
}

It does exsist though. If I add @ts-ignore before using these parameters, the code works okay.

I wounder, how can I create a class decorator, that would extend parent's type. I tried to do something like this:

export const DetachedWindow = (options: TDetachedWindowOptions = defaultOptions) => <R extends Constructable>(Clazz: R): R & { value: string } => {

But it didn't help.

Any ideas?

1 Answers

The answer is in the TypeScript docs of the Class decorators:

// Note that the decorator _does not_ change the TypeScript type
// and so the new property `reportingURL` is not known
// to the type system:
bug.reportingURL;
Property 'reportingURL' does not exist on type 'BugReport'.

Somewhat related: Angular uses decorators to annotate every class, like @Component which adds lifecycle hooks to the class, but as developers we still have to implement an interface to properly retrieve lifecycle hooks signatures.

That is how Angular's team solved this.

interface OnInit {
  ngOnInit(): void
}

@Component({
  selector:    'app-hero-list',
  templateUrl: './hero-list.component.html',
})
export class HeroListComponent implements OnInit {
  ngOnInit(): void {
    // Initialization code
  }
}
Related