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.
}
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.