microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.63k stars 12.44k forks source link

Missing JSDoc description when using arrow functions in --allowJs + --declaration #34860

Open hipstersmoothie opened 4 years ago

hipstersmoothie commented 4 years ago

I was trying to use --allowJs --declaration in a javascript only project and noticed that the JSDoc descriptions for arrow functions aren't properly attaching to the type defs.

TypeScript Version: 3.8.0-dev.20191031

Search Terms: arrow function, allowJs, declaration

Code

https://github.com/hipstersmoothie/typescript-jsdoc-bug

/**
 * This isn't included
 *
 * @param bar a test param
 */
export const foo = bar => {};

/**
 * This is included
 *
 * @param baz a test param
 */
export function fob(baz) {}

Expected behavior: (At least something like)

/**
 * This isn't included
 *
 * @param bar a test param
 */
export function foo(bar: any): void;
/**
 * This is included
 *
 * @param baz a test param
 */
export function fob(baz: any): void;

Actual behavior:

/**
 * This is included
 *
 * @param baz a test param
 */
export function fob(baz: any): void;
export function foo(bar: any): void;

If I switch the file to .ts the output is expected

/**
 * This isn't included
 *
 * @param bar a test param
 */
export declare const foo: (bar: string) => void;
/**
 * This is included
 *
 * @param baz a test param
 */
export declare function fob(baz: string): void;
orta commented 4 years ago

Related, here's a baseline test where all the exported members should have JSDoc. I spotted that the issue also occurs when you define the export on the module.exports directly

// @allowJs: true
// @declaration: true
// @filename: example.js

/** These will persist */
function myFunc() { return 34 }

/** My variable does something */
const exportedVariable = "exported"

/** This one should do something too */
const arrowFunction = () => 23

/** Defining before */
const assignedBefore = myFunc()

module.exports = {
  myFunc,
  exportedVariable,
  arrowFunction,
  /** These JSDoc annotations do not not persist */
  exampleWhichShouldHaveJSDocs: myFunc(),
  assignedBefore
}

=>

//// [example.d.ts]
/** These will persist */
export function myFunc(): number;
/** My variable does something */
export const exportedVariable: "exported";
export function arrowFunction(): number;
/** Defining before */
export const assignedBefore: number;
export declare const exampleWhichShouldHaveJSDocs: number;
PMudra commented 4 years ago

I have encountered the same issue as @hipstersmoothie and found two workarounds that might be helpful for now:

JavaScript Code

export const foo =
  /**
   * This is included
   *
   * @param bar a test param
   */
  bar => {};

/**
 * This is included
 *
 * @type {(bar: any) => void}
 */
export const foo2 = bar => {};

Declaration Output

/**
 * This is included
 *
 * @param bar a test param
 */
export function foo(bar: any): void;
/**
 * This is included
 *
 * @type {(bar: any) => void}
 */
export const foo2: (bar: any) => void;
turadg commented 6 months ago

This was scheduled four years ago for 3.8.1. @RyanCavanaugh is still something you think is worth fixing?

In https://github.com/Endojs/endo/ the modules are in .js and we emit .d.ts for NPM. The code makes heavy use of arrow functions so due to this bug much of the JSDoc is missing from the .d.ts.

mwaeckerlin commented 3 months ago

Is there still no solution? I'm still having the same issue with *.ts files:

jsdoc.json:

{
  "plugins": [
    "plugins/markdown",
    "jsdoc-escape-at",
    "jsdoc-ts-utils",
    "better-docs/typescript"
  ],
  "typescript": {
    "module": "ESNext",
    "target": "ES2022"
  },
  "source": {
    "include": [
      "src"
    ],
    "includePattern": "^.+\\.ts(x)?$"
  },
  "sourceType": "module",
  "opts": {
    "destination": "doc",
    "excludeExternals": true,
    "readme": "README.md",
    "package": "package.json"
  }
}

Sample documentation:

/**
 * Sorts an array of objects by specified keys.
 * @param {string} key - The primary key to sort by.
 * @param {...string} keys - Additional keys to sort by.
 * @returns {Function} A comparator function.
 * @example
 * const data = [{a: 2, b: 1}, {a: 1, b: 2}, {a: 1, b: 1}]
 * data.sort(sortByKey('a', 'b')) // [{a: 1, b: 1}, {a: 1, b: 2}, {a: 2, b: 1}]
 */
export const sortByKey = (key: string, ...keys) => (a: object, b: object) =>
  a[key] > b[key] ? 1 : a[key] < b[key] ? -1 : keys[0] ? sortByKey(keys[0], keys.slice(1))(a, b) : 0

Generated output:

(constant) sortByKey

Sorts an array of objects by specified keys.

Source: array.ts, line 11

Example

const data = [{a: 2, b: 1}, {a: 1, b: 2}, {a: 1, b: 1}]
data.sort(sortByKey('a', 'b')) // [{a: 1, b: 1}, {a: 1, b: 2}, {a: 2, b: 1}]

So not recognized as function, parameters and return value not documented.


For me, this works neither:

/**
 * Sorts an array of objects by specified keys.
 * @example
 * const data = [{a: 2, b: 1}, {a: 1, b: 2}, {a: 1, b: 1}]
 * data.sort(sortByKey('a', 'b')) // [{a: 1, b: 1}, {a: 1, b: 2}, {a: 2, b: 1}]
 */
export const sortByKey =
  /**
  *  @param {string} key - The primary key to sort by.
  * @param {...string} keys - Additional keys to sort by.
  * @returns {Function} A comparator function.
  */
  (key: string, ...keys) => (a: object, b: object) =>
    a[key] > b[key] ? 1 : a[key] < b[key] ? -1 : keys[0] ? sortByKey(keys[0], keys.slice(1))(a, b) : 0

This:

/**
 * Sorts an array of objects by specified keys.
 * @param {string} key - The primary key to sort by.
 * @param {...string} keys - Additional keys to sort by.
 * @returns {Function} A comparator function.
 * @example
 * const data = [{a: 2, b: 1}, {a: 1, b: 2}, {a: 1, b: 1}]
 * data.sort(sortByKey('a', 'b')) // [{a: 1, b: 1}, {a: 1, b: 2}, {a: 2, b: 1}]
 */
export function sortByKey(key: string, ...keys) {
  return function (a: object, b: object) {
    return a[key] > b[key] ? 1 : a[key] < b[key] ? -1 : keys[0] ? sortByKey(keys[0], keys.slice(1))(a, b) : 0
  }
}

Finally produces:

sortByKey(key, …keys) → {function}

Sorts an array of objects by specified keys.
Parameters:
Name    Type    Attributes  Description
key     string      

The primary key to sort by.
keys    string  <repeatable>

Additional keys to sort by.

Source:

        array.ts, line 11 

Returns:

A comparator function.

Type
    function 

Example

const data = [{a: 2, b: 1}, {a: 1, b: 2}, {a: 1, b: 1}]
data.sort(sortByKey('a', 'b')) // [{a: 1, b: 1}, {a: 1, b: 2}, {a: 2, b: 1}]

But I like the arrow syntax and want to use that...!

LeakedDave commented 1 week ago

O boi stumbling here to see this is a problem for half a decade. All you have to do is add @type

/**
 * Prints 'Hello World, ' + name to the console
 * @param {string} name
 * @type {(name: string) => void}
 */
export const helloWorld = (name) => {
    console.log('Hello World, ' + name);
}

And now it shows up.

/**
 * Prints 'Hello World, ' + name to the console
 * @param {string} name
 * @type {(name: string) => void}
 */
export const helloWorld: (name: string) => void;