Search code examples
javascriptvisual-studio-codeintellisensejsdoc

JSDoc - How do I document a static method in a superclass that returns an instance of its subclass?


So I have a base class kind of like this:

class BaseAPIClass {
    /**
     * Async factory
     * Does some async operation, then instantiates the inheriting class.
     *
     * @param {String} id
     *
     * @returns {Promise<???>}
     */
    static async create(id) {
        const data = await fetchFromNetwork(id)
        return new this.prototype.constructor(data)
    }
}

And a subclass kind of like this:

class RadAPIClass extends BaseAPIClass {
    /**
     * Output a compliment.
     * @param {name} [name='my dude']
     */
    complimentJorts(name = 'my dude') {
        const possibleCompliments = ['gnarly', 'tubular', 'bodacious', 'radical', 'sick']
        const i = Math.floor(Math.random() * possibleCompliments.length)
        const compliment = possibleCompliments[i]
        console.log(`Those are some ${compliment} jorts, ${name}!`)
    }
}

My question is this:

How do I document the return type for BaseAPIClass.create() in a way that correctly tells readers & intellisense that RadAPIClass.create() returns a {Promise<RadAPIClass>}?

async function main()
{
    const rad = await RadAPIClass.create()
    rad.complimentJorts() // I want VSCode to be able to autocomplete this method for me.
}

Solution

  • Older and wiser. The answer is a generic @this.

    class BaseAPIClass {
        /**
         * Async factory
         * Does some async operation, then instantiates the inheriting class.
         *
         * @template {{new(...args: any): any}} T
         * @this {T}
         *
         * @param {String} id
         *
         * @returns {Promise<InstanceType<T>>}
         */
        static async create(id) {
            const data = await fetchFromNetwork(id)
            return new this.prototype.constructor(data)
        }
    }
    

    The generic T is inferred from the type of the subclass because of @this {T}, so a return type of Promise<InstanceType<T>> (remember T is a typeof BaseAPIClass or typeof RadAPIClass and not an instance type) resolves to the correct class.