Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Circular reference error happens in jsdoc but not in typescript. #46369

Open
jespertheend opened this issue Oct 14, 2021 · 6 comments
Open

Circular reference error happens in jsdoc but not in typescript. #46369

jespertheend opened this issue Oct 14, 2021 · 6 comments
Labels
Bug A bug in TypeScript Domain: JSDoc Relates to JSDoc parsing and type generation Effort: Difficult Good luck. Help Wanted You can do this
Milestone

Comments

@jespertheend
Copy link
Contributor

Bug Report

πŸ”Ž Search Terms

circularly references itself jsdoc ts2456

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about circular

⏯ Playground Link

Playground link (js)
Playground link (ts)

πŸ’» Code

Foo.js

/** @typedef {Object.<string, Foo>} Foo */

Foo.ts

type Foo = {
    [x: string]: Foo;
};

πŸ™ Actual behavior

Type alias 'Foo' circularly references itself. ts(2456) happens in Foo.js. The TypeScript equivalent works fine.

πŸ™‚ Expected behavior

Both Foo.ts and Foo.js work without errors.

Related issues

I found some related issues but they are either closed or use a different example.
#39372 - Closed (fixed)
#45641 - Seems very similar but uses Array<> and typescript rather than jsdoc. I'm not sure if the root cause is the same so this might be a duplicate.

@andrewbranch andrewbranch added Bug A bug in TypeScript Domain: JSDoc Relates to JSDoc parsing and type generation labels Nov 2, 2021
@andrewbranch andrewbranch added this to the Backlog milestone Nov 2, 2021
@andrewbranch andrewbranch added Effort: Difficult Good luck. Help Wanted You can do this labels Nov 2, 2021
@junaga
Copy link

junaga commented Jan 21, 2022

I have the same issue.

@cedx
Copy link

cedx commented Apr 26, 2022

Same here (using TS 4.6.3).

@jespertheend
Copy link
Contributor Author

The workaround is to use TypeScript in the JSDoc comment, rather than Object.<string, Foo> notation:

/**
 * @typedef {{
 *  [x: string]: Foo
 * }} Foo
 */

(playground)

@cedx
Copy link

cedx commented Apr 26, 2022

@jespertheend It doesn't work every time.

// TypeScript: OK
export type Json = null | boolean | number | string | Json[] | {[property: string]: Json};
// JavaScript: error 2456
/**
 * @typedef {null | boolean | number | string | Json[] | {[property: string]: Json}} Json
 */

@jespertheend
Copy link
Contributor Author

Seems like that's because Json[] is also in there. Another way to work around this is to add an extra type just for the array:

/**
 * @typedef {null | boolean | number | string | JsonArray | {[property: string]: Json}} Json
 */

/** @typedef {Json[]} JsonArray */

(playground)

@kungfooman
Copy link

kungfooman commented Dec 4, 2022

I wrote a little template typedef to help creating circular references:

/**
 * @typedef {T | CircularArray<T> | CircularObject<T>} Circular
 * @template T
 */
/**
 * @typedef {Circular<T>[]} CircularArray
 * @template T
 */
/**
 * @typedef {{[key: string]: Circular<T>}} CircularObject
 * @template T
 */
/**
 * @typedef {Circular<number | string>} NumberStringObject
 */
/** @type {NumberStringObject} */
const x = {
    y: [1, {z: [1, 2, 3]}]
}

(playground)

Another example, lets say you have a type like this:

/**
 * @typedef {{destroy: Function} | HTMLElement | Destroyable[] | undefined | null} Destroyable
 */

Error:
image

Solution:

/**
 * @typedef {T | CircularArray<T>} Circular
 * @template T
 */
/**
 * @typedef {Circular<T>[]} CircularArray
 * @template T
 */
/**
 * @typedef {{destroy: Function} | HTMLElement | undefined | null} DestroyableTypes
 */
/**
 * @typedef {Circular<DestroyableTypes>} Destroyable
 */
/**
 * @param {Destroyable} o
 */
export function destroy(o) {
  if (!o) {
    return;
  }
  // Order of most to least specific types (every array is an object, but not vice versa)
  if (o instanceof Array) {
    // Destroy an array by destroying every component recursively and setting length to 0
    o.forEach(destroy);
    o.length = 0;
  } else if (o instanceof HTMLElement) {
    // TODO: maybe remove() with Removeable instead?
    o.remove();
  } else if (o instanceof Object && typeof o.destroy === "function") {
    o.destroy();
  } else {
    console.warn('destroy has no function for type', typeof o);
  }
}
const data = [{
  destroy() {
    console.log("oh no");
  }
}, {
  destroy() {
    console.log("aaaahhh");
  }
}, [
  {
    topkek() {
      console.log("grrraaaaaaaaaahh"); // console warns> destroy has no function for type object
    }
  }, {
    destroy() {
      console.log("*dies in silence*");
    }
  }
]];
destroy(data);

(playground)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Domain: JSDoc Relates to JSDoc parsing and type generation Effort: Difficult Good luck. Help Wanted You can do this
Projects
None yet
Development

No branches or pull requests

5 participants