I also wanted to extend the class with a function and developed a solution only for TypeScript. I'm not quite sure if this is a good idea, because smart decisions are not always good decisions. YMMV.
Thanks to Matthias Buleans for the partial answer! I build on that.
// same as in the answer of Mattias interface Spy { (foo: string, bar: number): boolean // Just an example wasCalled(): boolean } // and now for the real solution! class Spy { _wasCalled: boolean _baz: boolean // Just an example private constructor(baz: boolean) { this._wasCalled = false this._baz = baz } wasCalled(): boolean { return this._wasCalled } toString() { return '[object Spy]' } static create(baz: boolean) { const f = <Spy>function(this: Spy, foo: string, bar: number): boolean { // Do your thing here. Use f instead of this! console.log('wasCalled', f.wasCalled()) f._wasCalled = true } const spy = new Spy(baz) Object.assign(f, spy) Object.setPrototypeOf(f, Spy.prototype) return f } }
The idea is to create a function and an instance of Spy , and then assign a function to the prototype and properties. Return an instance from a static method. The bonus is the toString() method.
const spy = Spy.create(true) console.log('calling spy', spy('foo', 42)) console.log('instanceof', spy instanceof Spy)
works as expected.
I don't think new Spy() will work because we need to assign a function, not the other way around. And since we cannot replace this we cannot make this callable. The hypothetical way that I see is to extend the class with a truly constructor function, something like this: class Spy2 extends function() {} {} , but I did not find a way to make this work.
source share