JSDoc: Enum and class not documented correctly - javascript

I have the following class
/** My special class */
export default class MyClass {
/**
* An interesting value
* #readonly
* #enum {number}
*/
static TEST = {
/** Critical value */
BAR: 1,
/** Default value */
FOO: 2
}
}
Now, when I run jsdoc
$> /node_modules/.bin/jsdoc test.js
I see in the generated documentation
A Class named exports and Bar and Foo have became globals. The Enum TEST is a member of the class, but not related anymore with Bar and Foo. Any suggestions what I do wrong with jsdoc ?
(jsdoc enum)

Related

JSDoc ts annotations for generic-ish base class (VS Code)

I'm trying to use JSDoc annotations for a base class where a constructor param (and some getter/method return vals) will be the classes which extend the base class.
Is there a way to specify that #template must be derived from base class?
For context I'm writing an abstraction for file system (files + folders) where their name on disk may contain #tags - the bulk of code is the same, hence desire for base class, but there are some subtle differences depending on whether it's file or folder (eg. for files the .ext is after the #tags thus a file name needs slightly different parsing to a folder name). Additionally, there will be classes derived from the file/folder classes, eg. certain parts of the folder tree merit contextual helper methods.
/**
* #template TParent the parent item, or `null` if root item
* #type Base<TParent?, string, boolean>
*/
class Base {
/** #type {TParent?} parent item or `null` if root
#parent
/** #type {Set<string>} list of tags from parsed item name
#tags
/** #type {boolean} `true` if this item represents a file
#isFile
/**
* #constructor
* #param {TParent?} parent - the parent item, or `null` if root
* etc...
*/
constructor(parent, name, isFile = false) {
this.#parent = parent
this.#isFile = isFile
this.#tags = this.#parse(name)
}
/** #returns {TParent} parent item, or `null` if root
get parent() { return this.#parent }
get tags() { return this.#tags }
#parse(name) { return new Set() }
methodActsOnParent() {
const goo = this.parent?.tags.has("#Goo") ?? false
// ... ^ TParent might not have .tags
}
}
class Folder extends Base {
// folder specific stuff
constructor(...) { ... }
}
class File extends Base {
// file specific stuff
constructor(...) { ... }
}
// in some external file:
class Foo extends Folder {
// foo specific stuff
constructor(...) { ... }
/** #returns {TParent} */
get parent() { return this.parent }
}
Is there some way to say " TParent must be derived from Base " so the code hints, etc., know that the base properties/methods/etc will be available?
And, if I know class Foo will always have a parent (it's never root), is there some way to specify that, given that Base class allows null for parent? That way I could reduce null checking without resorting to // #ts-ignore cheats.
You can set a constraint on generic type by #template {boolean} T. This is the equivalent of TypeScript Foo<T extends boolean>
/**
* #template {Base<any> | null} T
*/
class Base {
/**#type {T}*/
#parent;
constructor(/**#type {T}*/parent) {
this.#parent = parent;
}
get parent() { return this.#parent; }
methodActsOnParent() {
this.parent.methodActsOnParent();
}
}
/**
* #template {Base<any> | null} T
* #extends {Base<T>}
*/
class Foo extends Base {
}
const b = new Base(new Base(null));
b.parent.parent;
b.parent.parent.methodActsOnParent();
const f = new Foo(new Foo(null));
f.parent.parent;
f.parent.parent.methodActsOnParent();

How to mark a property deprecated in a #typedef?

I would like to mark a property (for example, qux below) as deprecated:
/**
#typedef {object} Foo
#property {string} bar
#property {symbol} qux - How to deprecate it?
#deprecated #property {symbol} qux2 - Doesn't work
#property {symbol} qux3 #deprecated - This marks the whole of Foo as deprecated
*/
I should mention, I'd like to do this in a JSDoc comment, not using TypeScript.
As far as i know the #deprecated tag can be only used for deprecating anything else than an entire symbol
Following the jsdoc docs this is the only way you can use #deprecated
/**
* #deprecated since version 2.0.0
*/
function old() {
}
https://jsdoc.app/tags-deprecated.html
According to Issue#131 over at tsdoc, you can actually use #deprecated on properties, you just have to set it inside the object (works with JS as well).
For e.g.:
/**
* Explain the interface ...
*/
export interface test {
/**
* Foo property
*/
Foo: string;
/**
* #deprecated Use {#link test.qux2} instead.
*/
qux: string;
/**
* qux2 property
*/
qux2: string;
}
Results in:
In classes for e.g.:
/**
* Class description ...
*/
class User {
/**
* Holds user's name
*/
name = "";
/**
* Holds user's surname
*/
surname = "";
/**
* #deprecated Holds user height
*/
height = 0;
/**
* constructor
*/
constructor(name, surname) {
this.name = name;
this.surname = surname;
}
}
const me = new User("Arslan", "Sohail Bano");
Results:
When using the deprecated property.

How jsdoc describes static class method which returns SAME CLASS variable

This is very simple example of what I want to get. My question is about #returns tag. What should I write there?
class Base{
/**
* #returns {QUESTION: WHAT SHOUL BE HIRE??}
*/
static method(){
return new this()
}
}
class Sub extends Base{
}
let base= Base.method() // IDE should understand that base is instance of Base
let sub= Sub.method() // IDE should understand that sub is instance of Sub
There is no "relative" type.
You could fairly trivally modify the extended class;
/** #extends {Base} */
class Sub extends Base{
/** #returns {Sub} */
static method(){
return super.method();
}
}
Or perhaps use a third type, an #interface that defines this method's existence
/** #interface */
class I {
method() {}
}
/** #implements {I} */
class Base {
/** #returns {I} */
static method(){
return new this();
}
}
/**
* #extends {Base}
* #implements {I}
*/
class Sub {
/** #returns {I} */
static method(){
return new this();
}
}
I know that this is a very old question and that in most circumstances projects needing a polymorphic this for static methods would now be using TypeScript with the workaround as listed here.
Several of our browser based projects are still using pure ES module JavaScript without any build step and introducing a build step for this use case seems over the top. On these projects we are also using Visual Studio Code's 'Implicit Project Config: Check JS' to enable type checking. Having to cast the return of every extended static method is a real pain!
The following workaround works well in Visual Studio Code for JavaScript with JSDoc:
/**
* #template T
* #param {T} Class
* #returns {{
* method: () => InstanceType<T>
* } & T}
*/
// #ts-ignore
const fixPolymorphicThis = Class => Class
class Base {
static method() {
return new this()
}
}
const Sub = fixPolymorphicThis(class Sub extends Base { })
let base = Base.method() // IDE should understand that base is instance of Base
let sub = Sub.method() // IDE should understand that sub is instance of Sub
console.assert(sub instanceof Base)
console.assert(sub instanceof Sub)

How to specify template type of inherited class?

Consider a generic collection:
/**
* A collection of items of the same type
* #template TSingleItem
* */
class ItemsCollection {
/** #type {TSingleItem[]} **/
get items() {
return [createSingleItem()];
}
/**
* Creates a new item based on the implementation type
* #param {string} param
* #returns {TSingleItem}
*/
createSingleItem(param) {
throw new Error("Pure virtual method call!");
}
}
Now we implement it as:
class Item {
constructor(name) {
this.name = name;
}
}
class Items extends ItemsCollection {
createSingleItem(param) {
return new Item(param);
}
}
How do I tell JSDoc to assume that items on that inherited class is Item[] not TSingleItem[]?
I tried this above the class:
/**
* #extends {ItemsCollection<Item>}
* */
class Items extends ItemsCollection
That didn't help in visual studio at least. What's the correct syntax? Bonus points if it works with Visual Studio intellisense.
Turns out this is the correct code, as also seen in this nice tutorial:
/**
* #extends {ItemsCollection<Item>}
* */
class Items extends ItemsCollection
It does also work in Visual Studio 2017, just took a while to catch on. I'm leaving this Q&A instead of deleting because it wasn't exactly obvious or easy to google to confirm it.

JSDoc, treat POJO as instance of Class

I have a class and a function that takes instance of that class or a similar POJO object as argument.
I want to annotate this function using JSDoc.
class Test {
constructor(a, b) {
this.a = a;
this.b = b;
}
}
/**
* #param {Test} test
*/
function handleTest(test) {
console.log(test.a, test.b);
}
// Webstorm complains that argument is not of type Test
handleTest({
a: 'this is a'
});
Using #param {Test} test almost works... but WebStorm complains that POJO isn't assignable to type Test.
Is there some JSDoc trick I can do to make it clear that both instance of Test and a Test-like object are both OK?
Use type union:
/**
* #param {Test|{a,b}} test
*/
function handleTest(test) {
console.log(test.a, test.b);
}
The |{a,b} part of the type denotes separate allowable types. You could just use {Test|{}} but then the IDE doesn't know that a and b are expected properties.
That's the shorthand version; you could also define what "Test-like" objects are in case you plan on using and documenting TestLike objects in more than one place:
/**
* #typedef {object} TestLike
* #property {string} a - some property a
* #property {string} b - some property b
*/
/**
* #param {Test|TestLike} test
*/
handleTest(test) {
// ...
}

Categories

Resources