Bradley Simard
Bradley Simard

Reputation: 13

VSCode Javascript - ES6 Class Instance Generics JSDOC

I've recently found out that you can implement JSDoc generics within VSCode for a javascript project, which is fantastic. A big issue I've had with writing JS in the past is having the intellisense never really knowing which types I'm dealing with.

I followed this documentation on how to implement generics (using the @template annotation): https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#template

It works mostly as in I can properly see my class type. The problem is that it doesn't actually seem to know about any of the non-static methods and properties.

Here's some example code:

"use strict";

class Parent {
    age = 10;

    outputAge() {
        console.log(`My age is ${this.age}`);
    }
}

class Child extends Parent {
    name = "Bob";

    outputName() {
        console.log(`My name is ${this.name}`);
    }
}

/**
 * @template T
 * @param {T} classType
 * @returns {T}
 */
function genericFunction(classType) {
    return new classType();
}

const testGeneric = genericFunction(Child);
testGeneric.outputName();
testGeneric.outputAge();

The code executes properly. testGeneric correctly has the outputAge() and outputName() methods at runtime.

The problem is that VSCode doesn't seem to know about:

Screenshot of it not showing the fields

I suspect this is because the way the generics work. It's seemingly knowing it's a type Child, but not that it's an instance of Child. If I added any static methods, they'd correctly show up in the intellisense.

If I inline annotate testGeneric to have a type of Child, I can see all the properties and methods:

Screenshot of it working

My question to you all: Is there a way to correctly setup my template to know it returns an instance of T or is this just a limitation/bug in VSCode's intellisense?

Let me know if anything needs clarification.

Upvotes: 1

Views: 741

Answers (2)

tenshi
tenshi

Reputation: 26307

Since I couldn't find anything equivalent to ReturnType for JSDoc, I've decided to do the other way, and make the parameter return T, so that @returns {T} now returns the instance type:

/**
 * @template T
 * @param {{ new(...args: any[]): T }} classType
 * @returns {T}
 */
function genericFunction(classType) {
    return new classType();
}

Tested with https://vscode.dev/ and typescriptlang.org/play

Upvotes: 1

SwaD
SwaD

Reputation: 641

If you want to use @template, then it seems to me that it would be more correct

/**
* @template ParentType
* @property {number} age
* @property {function(): void} outputAge
*/
class Parent {
    age = 10;

    outputAge() {
        console.log(`My age is ${this.age}`);
    }
}

/**
* @template ChildType
* @extends {Parent}
* @property {string} name
* @property {function(): void} outputName
*/
class Child extends Parent {
    name = "Bob";

    outputName() {
        console.log(`My name is ${this.name}`);
    }
}

/**
* @template {ChildType} T
* @param {T} classType
* @returns {T}
*/
function genericFunction(classType) {
    return new classType();
}

const testGeneric = genericFunction(Child);
testGeneric.outputName();
testGeneric.outputAge();

Upvotes: 0

Related Questions