π Search Terms
jsdoc typedef declaration order, typedef hoisted end of file, callback comment lost, allowjs declaration emit typedef comment
π Version & Regression Information
Reproduces identically on 5.9.3 and 7.0.2 β not a regression. I searched the tracker and couldn't find an exact match; closest are #62453 ("JSDoc comments emitted a second time") and the now-closed #62979 ("Fix duplicate JSDoc @typedef and @callback comments"), but those describe duplication, whereas this is a misplacement/loss.
β― Playground Link
(paste either repro below into the TypeScript Playground, switch to "JS" mode, enable "declaration" in the config gear, and check the .D.TS tab)
Repro 1 β @typedef gets hoisted to the end of the file, comment does not move with it:
export function noop() {}
/**
* @typedef {Object} Point
* @property {number} x
* @property {number} y
*/
/**
* @param {Point} p
*/
export function dist(p) {
return p.x;
}
Compiled with: tsc --allowJs --declaration --emitDeclarationOnly a.js
Repro 2 β @callback stays in its correct position, but still loses its comment:
export function noop() {}
/**
* @callback Comparator
* @param {number} a
* @param {number} b
* @returns {number}
*/
/**
* @param {number[]} arr
* @param {Comparator} cmp
*/
export function sortWith(arr, cmp) {
return arr.slice().sort(cmp);
}
Actual output:
```ts
export declare function noop(): void;
export type Comparator = (a: number, b: number) => number;
/**
Here Comparator lands in the correct position (right after noop), but its doc comment is left behind above sortWith instead of attached to the type it describes.
When a JSDoc @typedef or @callback is preceded by another top-level declaration in the source file, declaration emit separates the JSDoc comment from the type it documents.
For Repro 1, the actual output is:
export function noop(): void;
/**
* @typedef {Object} Point
* @property {number} x
* @property {number} y
*/
/**
* @param {Point} p
*/
export function dist(p: Point): number;
export type Point = {
x: number;
y: number;
};
Point is emitted at the very end of the file, but its comment is left in its original position, directly above dist. Result: dist looks documented with a comment that actually describes Point, and Point itself has no documentation.
For Repro 2, the actual output is:
export declare function noop(): void;
export type Comparator = (a: number, b: number) => number;
/**
* @callback Comparator
* @param {number} a
* @param {number} b
* @returns {number}
*/
/**
* @param {number[]} arr
* @param {Comparator} cmp
*/
export declare function sortWith(arr: number[], cmp: Comparator): number[];
Here Comparator lands in the correct position (right after noop), but its comment is still left behind above sortWith instead of attached to Comparator.
In both cases the underlying issue is the same: whatever step attaches leading comments to declarations during .d.ts emission is not correctly following the type when it's synthesized from a JSDoc tag, once there's a preceding sibling declaration to get confused with. This is wrong because .d.ts output is documentation-facing β consumers read these comments in editor hover tooltips β so misattributed or missing docs are a real, user-visible regression, not a cosmetic quirk.
The JSDoc comment for a @typedef or @callback should stay attached to the declaration it documents in the emitted .d.ts, regardless of what other declarations appear earlier in the file.
For Repro 1, I'd expect either Point to keep its natural position in the output (immediately after noop, with its comment directly above it), or, if it must be hoisted to the end, its comment should be hoisted along with it β the two should never be split apart.
For Repro 2, I'd expect the Comparator type (already emitted in the correct position) to carry its own comment with it, rather than leaving that comment stranded above sortWith.
This matters because declaration emit exists to preserve type information and documentation for consumers of a package β someone hovering over Point or Comparator in their editor should see the description of that type, not nothing, and someone hovering over dist or sortWith shouldn't see documentation written for a different symbol.
Note: this only reproduces when at least one other top-level declaration precedes the @typedef/@callback block. A file with only the tag and the function using it emits correctly.
π Search Terms
jsdoc typedef declaration order, typedef hoisted end of file, callback comment lost, allowjs declaration emit typedef comment
π Version & Regression Information
Reproduces identically on 5.9.3 and 7.0.2 β not a regression. I searched the tracker and couldn't find an exact match; closest are #62453 ("JSDoc comments emitted a second time") and the now-closed #62979 ("Fix duplicate JSDoc
@typedefand@callbackcomments"), but those describe duplication, whereas this is a misplacement/loss.β― Playground Link
(paste either repro below into the TypeScript Playground, switch to "JS" mode, enable "declaration" in the config gear, and check the .D.TS tab)
Repro 1 β
@typedefgets hoisted to the end of the file, comment does not move with it:Compiled with:
tsc --allowJs --declaration --emitDeclarationOnly a.jsRepro 2 β
@callbackstays in its correct position, but still loses its comment:Actual output:
```ts
export declare function noop(): void;
export type Comparator = (a: number, b: number) => number;
/**
/
/*
*/
export declare function sortWith(arr: number[], cmp: Comparator): number[];
```
Here
Comparatorlands in the correct position (right afternoop), but its doc comment is left behind abovesortWithinstead of attached to the type it describes.When a JSDoc
@typedefor@callbackis preceded by another top-level declaration in the source file, declaration emit separates the JSDoc comment from the type it documents.For Repro 1, the actual output is:
Pointis emitted at the very end of the file, but its comment is left in its original position, directly abovedist. Result:distlooks documented with a comment that actually describesPoint, andPointitself has no documentation.For Repro 2, the actual output is:
Here
Comparatorlands in the correct position (right afternoop), but its comment is still left behind abovesortWithinstead of attached toComparator.In both cases the underlying issue is the same: whatever step attaches leading comments to declarations during .d.ts emission is not correctly following the type when it's synthesized from a JSDoc tag, once there's a preceding sibling declaration to get confused with. This is wrong because .d.ts output is documentation-facing β consumers read these comments in editor hover tooltips β so misattributed or missing docs are a real, user-visible regression, not a cosmetic quirk.
The JSDoc comment for a
@typedefor@callbackshould stay attached to the declaration it documents in the emitted .d.ts, regardless of what other declarations appear earlier in the file.For Repro 1, I'd expect either Point to keep its natural position in the output (immediately after
noop, with its comment directly above it), or, if it must be hoisted to the end, its comment should be hoisted along with it β the two should never be split apart.For Repro 2, I'd expect the Comparator type (already emitted in the correct position) to carry its own comment with it, rather than leaving that comment stranded above
sortWith.This matters because declaration emit exists to preserve type information and documentation for consumers of a package β someone hovering over
PointorComparatorin their editor should see the description of that type, not nothing, and someone hovering overdistorsortWithshouldn't see documentation written for a different symbol.Note: this only reproduces when at least one other top-level declaration precedes the
@typedef/@callbackblock. A file with only the tag and the function using it emits correctly.