Repository: TypeScript
Issue: Proposal: JSDoc tag to specify preferred type
Labels: Suggestion,Awaiting More Feedback

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](https://github.com/microsoft/TypeScript/issues/49216) and suggestions from Discord [1] I propose an explicit jsdoc tag to specify the preferred type:

```ts
/**
 * @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: 

```ts
/**
 * @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:

```js
/**
 * @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:

```ts
/**
 * @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:

```ts
/**
 * @suggest {['a', 'b', 'c']} p
 */
```


2. 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,

```ts
/**
 * @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.

3. Should the suggestion be integrated on the end of existing tags?

That is,

```js
/**
 * @param {string} p {'a' | 'b'}
 */
```

I can't think of a good syntax for this.

4. 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.