microsoft / TypeScript

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

JSDoc support for @yields #23857

Open SergioMorchon opened 6 years ago

SergioMorchon commented 6 years ago

It would be nice to be able to use the @yields JSDoc tag.

We work with JS and JSDoc, but taking advantage of TSC and VSCode. We also use the valid-jsdoc eslint rule.

We have:

/** @typedef Thing (...) */

function* walkThings() {
//... some yield here and there
}

We want to

/**
 * @yields {Thing}
 */
function* walkThings() {
//... some yield here and there
}

We can't do

/**
 * @returns {IterableIterator<Something>}
 */
function* walkThings() {
//... some yield here and there
}

Because it doesn't return, but yields, and eslint complains about it. We can disable the rule here, but it is not the desirable scenario.

DanielRosenwasser commented 6 years ago

Just to weigh in here, sounds like a bug on the ESLint side as well.

SergioMorchon commented 6 years ago

Just to weigh in here, sounds like a bug on the ESLint side as well. I'm not really confident about that @DanielRosenwasser :

  • return means something different from yield at a syntactic level.
  • The IterableIterator<T> is an implementation detail, the code does not return an iterable iterator, nor the developer wrote that. Just multiple yields.
  • I couldn't find any standard documentation about what typescript defined as IterableIterator, so I wouldn't expect from eslint to know about it. Just curious about it: where does it comes from? Is it an internal definition for typescript generators?
weswigham commented 6 years ago

Is it an internal definition for typescript generators?

It's an interface (well, suite of them) defining the shape of a generator or IterableIterator spec object:

interface IteratorResult<T> {
    done: boolean;
    value: T;
}

interface Iterator<T> {
    next(value?: any): IteratorResult<T>;
    return?(value?: any): IteratorResult<T>;
    throw?(e?: any): IteratorResult<T>;
}

interface Iterable<T> {
    [Symbol.iterator](): Iterator<T>;
}

interface IterableIterator<T> extends Iterator<T> {
    [Symbol.iterator](): IterableIterator<T>;
}
SergioMorchon commented 6 years ago

Thank you @weswigham for the response. I already noticed that (that's the reason why I used it in the JSDoc part). I wanted to know if there were such definition in a standard way, just like HTMLElement does, or if it was a definition made by the typescript developers to model the iterable iterator instances (thinking about PromiseLike<T>).

You solved my question anyway, thanks again!

clshortfuse commented 1 year ago

The eslint rule in the original comment is now deprecated, but https://github.com/gajus/eslint-plugin-jsdoc can be satisfied with @yield (at least on the recommended ruleset). Typescript needs @return. Both can be used, but it's not really ideal:

/**
 * @template {boolean} [E=null]
 * @template {boolean} [A=null]
 * @template {boolean} [T=null]
 * @template {boolean} [C=null]
 * @param {Node} root Root node to walk
 * @param {Object} filter Tree walking options
 * @param {E} [filter.element] Yield elements
 * @param {A} [filter.attribute] Yield attributes
 * @param {T} [filter.text] Yield Text node
 * @param {C} [filter.comment] Yield Comment nodes
 * @yields {
 *  (E extends null ? null : Element) |
 *  (A extends null ? null : Attr) |
 *  (T extends null ? null : Text) |
 *  (C extends null ? null : Comment)
 * }
 * @return {Iterable<
 *  (E extends null ? null : Element) |
 *  (A extends null ? null : Attr) |
 *  (T extends null ? null : Text) |
 *  (C extends null ? null : Comment)
 * >} Iterable
 */
export function* iterateNodes(root, filter = {}) {

I have TS syntax in my JSDoc, so I have disabled "jsdoc/valid-types". Other than that, eslint is happy.

Edit: Intellisense (TS) parses it fine as well.

image

Tooltip documentation bug is filed here: https://github.com/microsoft/vscode/issues/164888

Edit2: I think I'm supposed to use Generator<T> instead of Iterator<T>.

AnrDaemon commented 1 year ago

I think I'm supposed to use Generator<T> instead of Iterator<T>.

Seems so. Works on my side like that.

/** Runs `forEach()` callback over warehouses cache
 *
 * @returns {Generator<EqShowItem>}
 */
export function* forEachWarehouse() {
    // …
}

And then [...module.forEachWarehouse()].filter((wh) => …) correctly infer type.