Can you extend a function in TypeScript?

Yes, the TypeScript handbook calls this a "hybrid type", because it's a combination of a function type and a regular interface.

interface Spy {
    (foo: string, bar: number) : boolean; // Just an example
    wasCalled() : boolean;
}

var spy : Spy = createASpySomehow();
var result = spy("foo", 123);
if (spy.wasCalled()) {
    // ...
}

I wanted to extend a class with a function, too, and worked out a TypeScript-only solution. I am not really sure whether this is a good idea because clever solutions are not always good solutions. YMMV.

Thanks to Mattias Buelens to provide a partial answer! I am building on it.

// 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 the instance of Spy, then assign both the prototype and the properties to the function. Return the instance from a static method. A 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 that new Spy() would work because we need to assign to a function not the other way round. And because we can't replace this we can't make this a callable. A hypothetical way I see is to extend a class with really a function constructor somehow like this: class Spy2 extends function() {} {}, but I didn't find a way to get this working.

Tags:

Typescript