question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proposal: JSDoc tag to specify preferred type

See original GitHub issue

Authors often want to specify that a parameter may be passed any string, but has a set of preferred values. Right now, Typescript has no concept of ‘preferred type’, even though people try to indicate by unioning it with the declared type: string | 'a' | 'b' | 'c'. However, the compiler reduces this to string early on. People have used workarounds to fool the subtype reduction machinery, like string & {} | 'a', but PRs like https://github.com/microsoft/TypeScript/pull/49119 improve reduction and unintentionally break these workarounds from time to time.

Based on discussion from the May 20 2022 Design Meeting and suggestions from Discord [1] I propose an explicit jsdoc tag to specify the preferred type:

/**
 * @suggest {'foo' | 'bar'} e
 */
function f(e: string) {
}

This will instruct the language service to offer completions for e that wouldn’t be possible given just its declared type. Documentation generators can also display the preferred type along with the declared type.

The syntax is similar to @param: @suggest { type } identifier

But it can be used with any declaration:

/**
 * @suggest {'foo' | 'bar'} T
 */
function f<T>(p: T): T {
}
/** @suggest {'a' | 'b'} x */
declare var x: string
interface I {
  /** @suggest {'a' | 'b'} p */
  p: string
}
const o: I = {
  /**
   * @suggest {'a' | 'b' | 'c'} p Suggestions can conflict
   */
  p: 'a'
}
declare class C {
  /**
   * @suggest {HTMLAnchorElement | HTMLDivElement} p
   */
  p: HTMLElement
}

In Javascript, usage is more redundant:

/**
 * @param {string} p
 * @suggest {'a' | 'b'} p
 */
function f(p) {
}

In Typescript and checkJs files, the compiler should issue an error if the suggested type is not a subtype of the declared type:

/**
 * @suggest {'a' | 'b' | 0} p
//          ^^^^^^^^^^^^^ Suggested type must be a subtype of the declared type
 */
function f(p: string) {
}

And if the suggested type is never, then nothing should be added to the suggestion list. Maybe documentation generators should treat this as a signal that the property is deprecated (although – why not use @deprecated in that case?).

Other options:

  1. Do nothing. Make sure at least one workaround remains to make the compiler leave reducible unions unreduced.
  2. Directly support string | 'a' | 'b' | 'c': have the compiler create a new string type for every string used in a literal union. Proposed in #33471.

Other design questions

  1. Should the tag specify values instead of types? This seems clunky for the common case, a list of values:
/**
 * @suggest {['a', 'b', 'c']} p
 */
  1. Could the tag be simplified by allowing it only after another tag for a declaration?

The syntax could be simplified to @suggest { type } in that case. That is,

/**
 * @param p @suggest {'a' | 'b'}
 */

But this is awkward in Typescript where people aren’t used to writing @template for type parameters, for example. And few other JSDoc tags are context-sensitive like this – @typedef/@property is the main one, and it’s notoriously hard to use.

  1. Should the suggestion be integrated on the end of existing tags?

That is,

/**
 * @param {string} p {'a' | 'b'}
 */

I can’t think of a good syntax for this.

  1. This is the first tag that TS users would be using to specify types in JSDoc. Is this OK?

Tag name

The tag is intended to specify a subtype of the declared type. Tools besides the compiler will use the type as an auxiliary to the declared type. Here are a few ideas for names:

  • @prefer
  • @suggest
  • @suggest-type
  • @suggesttype
  • @suggestType
  • @intended
  • @expected

[1] Thanks to cspotcode, @Gerrit0, Andrew Kay, d-fischer and Elijah from #language-design.

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:31
  • Comments:16 (13 by maintainers)

github_iconTop GitHub Comments

4reactions
sandersncommented, May 24, 2022

@orta ah, that’s the other option. I linked to it in the proposal.

Also, you can see @fatcerberus suggesting a JSDoc solution 3 years ago. Guess we’re just slow on the uptake.

2reactions
xirzeccommented, May 24, 2022

I love this feature idea, since today we end up having to export enums (of one kind or another) of ‘known’ values in the Azure SDKs when we have what is referred to internally as an “extensible enum” (the service may later add values) – this seems much cleaner and is still something we can auto-generate

Read more comments on GitHub >

github_iconTop Results From Across the Web

Use JSDoc: @type
The @type tag allows you to provide a type expression identifying the type of value that a symbol may contain, or the type...
Read more >
Type-safe JavaScript code with JsDoc - Prisma
JsDoc is a great tool for documenting code and providing type-safety in your JavaScript project without any additional configuration.
Read more >
Google JavaScript Style Guide
9 Appendices: 9.1 JSDoc tag reference: 9.2 Commonly misunderstood style rules ... but other types of conventions or coding standards as well.
Read more >
eslint-plugin-jsdoc - npm
The defaults in eslint-plugin-jsdoc (for tags which offer aliases) ... option map to indicate preferred or forbidden types (if default types ...
Read more >
eslint-plugin-jsdoc - npm Package Health Analysis - Snyk
tagNamePreference to configure a preferred alias name for a JSDoc tag. ... The defaults in eslint-plugin-jsdoc (for tags which offer aliases) are as...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found