microsoft / microsoft/TypeScript

Function parameters are not inferable when defined via JSDoc using @type tag (with strict)

Open
#58,580 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Domain: JSDoc Help Wanted Possible Improvement
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

🔎 Search Terms

infer
@type
strict
JSDoc
Parameters
Function

🕗 Version & Regression Information
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about type inference.

I've pulled the latest version of this repository and made a test case in the "fourslash" section, which demonstrates the problem I see.
(I couldn't see how to do something like this in the playground, sorry. Commit is linked)

⏯ Playground Link

https://github.com/scottmcginness/TypeScript/commit/cda5366448cb994866517834e0c1ca78b10042cb

💻 Code
// In file func.js
export function func(/** @type {string} */ param) {};
// In file use-it.js
import { func } from "./func.js";
type FuncParam = (typeof func) extends (...args: infer P) => any ? P : never;
//   ^ never, but expected [param: string]

In tsconfig.json:

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "noEmit": true,
    "strict": true
  }
}

(The above definition is obviously just Parameters<T>, but put in full for comparison with another example below)

🙁 Actual behavior

The type FuncParam resolved to never (i.e. it took the false branch of the ternary)

🙂 Expected behavior

The type FuncParam should be [param: string], as given by the JSDoc @type tag

Additional information about the issue

This seems like a bug because all other ways of specifying the func function with JSDoc or the FuncParam type seemed to work as expected:

  • Using the @param tag instead:
/**
 * @param {string} param
 */
export function func(param) {};

yields FuncParam[param: string]

  • Using a union with undefined works, but is not what I want for the definition of func:
export function func(/** @type {string | undefined} */ param) {};

yields FuncParam[param?: string | undefined]

  • Trying the conditional without infer works (but is not usable for the intended purpose):
import { func } from "./func.js";
type FuncIsAFunc = (typeof func) extends (...args: any) => any ? 'good' : never;
//   ^ 'good'
  • Pulling just the first parameter out also works (but obviously that's not how Parameters<T> works):
import { func } from "./func.js";
type FuncParam = (typeof func) extends (arg: infer A) => any ? A : never;
//   ^ string

This also only seemed to occur specifically with strict: true. I couldn't see this with any of the other strict... options (though I may have missed something here)


All other views on the function seems to show that it is happily a function with a string parameter. i.e. the tooltip hover over func, while inside use-it.ts shows:

(alias) function func(param: string): void
import func

The output I see from the single test linked above (using hereby runtests --tests=jsDocInferredFunctionParameters) is:

  1) fourslash tests
       tests/cases/fourslash/jsDocInferredFunctionParameters.ts
         fourslash test jsDocInferredFunctionParameters.ts runs correctly:

      AssertionError: At marker '': quick info text: expected 'type FuncParam = never' to equal 'type FuncParam = [param: string]'
      + expected - actual

      -type FuncParam = never
      +type FuncParam = [param: string]

      at _TestState.verifyQuickInfoString (src\harness\fourslashImpl.ts:1863:16)
      at Verify.quickInfoIs (src\harness\fourslashInterfaceImpl.ts:268:20)
      at eval (jsDocInferredFunctionParameters.js:13:8)
      at runCode (src\harness\fourslashImpl.ts:4618:9)
      at runFourSlashTestContent (src\harness\fourslashImpl.ts:4576:5)
      at runFourSlashTest (src\harness\fourslashImpl.ts:4559:5)
      at Context.<anonymous> (src\testRunner\fourslashRunner.ts:59:39)
      at processImmediate (node:internal/timers:476:21)

This also happens for arrow functions and class methods, e.g.

// In func.js
export const func = (/** @type {string} */ param) => {};
export class Cls{
  method(/** @type {string} */ param) {}
}

with similar code as for FuncParam.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with tests/cases/fourslash/jsDocInferredFunctionParameters.ts and run it with the reported hereby command to reproduce the quick-info failure. Trace the TypeScript checker behavior for JavaScript parameters annotated with JSDoc @type under strict mode, including arrow functions and class methods. Done means the test reports [param: string] rather than never for the inferred parameter tuple.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.