#2144·jsdoc

Static class properties incorrectly parsed with instance scope instead of static scope

Author: Haixing-HuCreated Aug 24, 2025Updated Apr 29, 2026

Bug Description

JSDoc incorrectly parses static class properties with instance scope (#) instead of static scope (.), resulting in wrong longnames and scope classification.

Expected Behavior

Static class properties should be parsed with:

  • Longname: ClassName.propertyName (using . separator)
  • Scope: static

Actual Behavior

Static class properties are parsed with:

  • Longname: ClassName#propertyName (using # separator)
  • Scope: instance

Minimal Test Case

Input JavaScript:

javascript
/** Sample class with static and instance properties. */
class TestClass {
    /** Instance property. */
    instanceProp = 'instance';

    /** Static property. */
    static staticProp = 'static';

    /** Static method (works correctly). */
    static staticMethod() {
        return 'static method';
    }
}

Expected JSDoc Output:

  • TestClass#instanceProp with scope instance
  • TestClass.staticProp with scope static ❌ (currently fails)
  • TestClass.staticMethod with scope static

Actual JSDoc Output:

  • TestClass#instanceProp with scope instance
  • TestClass#staticProp with scope instance ❌ (incorrect)
  • TestClass.staticMethod with scope static

Environment

  • JSDoc version: 4.x (latest)
  • Node.js version: 23.1.0
  • Operating System: MacOS 15.6.1 (24G90)

Root Cause Analysis

The bug is located in packages/jsdoc-parse/lib/parser.js at lines 366-373 in the astnodeToMemberof() function:

javascript
} else if (isClassProperty(node)) {
  doclet = this._getDocletById(node.enclosingScope.nodeId);

  if (!doclet) {
    result.memberof = LONGNAMES.ANONYMOUS + SCOPE.PUNC.INSTANCE;
  } else {
    result.memberof = doclet.longname + SCOPE.PUNC.INSTANCE;  // ❌ Always uses INSTANCE
  }
}

This code always uses SCOPE.PUNC.INSTANCE (#) for class properties, regardless of whether they are static or not.

Proposed Fix

The fix should check the node.static property, similar to how static methods are handled in lines 384-390:

javascript
} else if (isClassProperty(node)) {
  doclet = this._getDocletById(node.enclosingScope.nodeId);

  if (!doclet) {
    result.memberof = LONGNAMES.ANONYMOUS + (node.static === true ? SCOPE.PUNC.STATIC : SCOPE.PUNC.INSTANCE);
  } else {
    result.memberof = doclet.longname + (node.static === true ? SCOPE.PUNC.STATIC : SCOPE.PUNC.INSTANCE);
  }
}

Test Case

I can provide a complete test case that demonstrates this bug:

Test fixture: test/fixtures/staticproperties.js fixtures-staticproperties.js Test spec: test/specs/documentation/staticproperties.js specs-staticproperties.js

The test shows that:

  • Static methods are parsed correctly ✅
  • Static getters/setters are parsed correctly ✅
  • Static properties are parsed incorrectly ❌

Impact

This bug affects:

  • Documentation generation for classes with static properties
  • Template engines that rely on correct scope classification
  • Any tooling that processes JSDoc output and expects correct longnames

Additional Notes

  • Static methods and static getters/setters work correctly
  • Only static properties (field declarations) are affected
  • The bug has been confirmed with a minimal reproducible test case