Skip to content

Regression in 7.1: contextual parameter type is lost for a destructuring pattern with a default (TS7031) #65011

Closed as duplicate

Description

@mkpoli

🔎 Search Terms

TS7031, implicit any, destructuring parameter, default value, JSDoc returns, checkJs, contextual typing

🕗 Version & Regression Information

  • This is a regression. It works in tsc 5.9.3 and in 7.0.2, and fails from 7.1 onwards:
version a.js
typescript 5.9.3 exit 0
typescript 7.0.2 exit 0
typescript 7.1.0-beta (gitHead 3e9236361fa97a9e6c685f4833ac457ae2d8c50a) TS7031, exit 2
typescript@next 7.1.0-dev.20261029.1 TS7031, exit 2

The Go implementation from that commit (tsc inside typescript@7.1.0-beta) behaves the same, so this is not specific to one host.

💻 Code

A checkJs file. The object literal is contextually typed by a JSDoc @returns; the method's second parameter is a destructuring pattern with a default.

tsconfig.json:

{ "compilerOptions": { "strict": true, "noEmit": true, "allowJs": true, "checkJs": true, "target": "es2022" }, "files": ["a.js"] }

a.js:

/** @typedef {{ signal?: AbortSignal }} Opts */
/** @typedef {{ structure: (c: string, o?: Opts) => Promise<string[]> }} Adapter */
/** @returns {Adapter} */
export function make() {
  return {
    async structure(c, { signal } = {}) {
      signal?.throwIfAborted()
      return [c]
    },
  }
}

The same happens when the function is a cast target rather than a returned object member:

/** @typedef {(n: number, o?: Opts) => Promise<void>} Fn */
export const p = /** @type {Fn} */ (async (n, { signal } = {}) => {
  signal?.throwIfAborted()
})

🙁 Actual behavior

a.js(6,26): error TS7031: Binding element 'signal' implicitly has an 'any' type.

🙂 Expected behavior

No error: signal is typed AbortSignal | undefined from the contextual Opts, as in 5.9 and 7.0.

Notes

  • Destructuring is not generally broken. Removing the = {} default keeps it working, and so does taking a named parameter and destructuring it in the body:
    async structure(c, opts) { const { signal } = opts ?? {}; signal?.throwIfAborted(); return [c] }
  • Tagging the parameters explicitly makes 7.1 accept the method again, so a workaround exists for callers who hit it:
    /** @param {string} c @param {Opts} [o] */
    async structure(c, { signal } = {}) { … }
  • Not specific to JavaScript or JSDoc. The same shape in .ts fails too:
    type O = { signal?: AbortSignal }
    const f: (o?: O) => void = ({ signal } = {}) => { signal?.throwIfAborted() }
    7.1.0-beta reports TS7031 here; 5.9.3 and 7.0.2 do not.
  • Found in a checkJs library that needed it on seven methods. Reported through a port that quotes this compiler against the pinned commit, JS: destructured parameter loses contextual typing from a JSDoc @returns type and reports TS7031 oxc-project/tsrs#847.

Activity

  1. changed the title [-]JS regression in 7.1: JSDoc contextual parameter type is lost for a destructuring pattern with a default (TS7031)[/-] [+]Regression in 7.1: contextual parameter type is lost for a destructuring pattern with a default (TS7031)[/+] on Oct 29, 2026
  2. timurzvural commented on Oct 29, 2026

    @timurzvural

    Opened an alternative fix in #65016.

    It skips object/tuple padding in checkDeclarationInitializer when the parameter already has a contextual type (same idea as #64978), also guards array binding patterns, and adds compiler tests for the JSDoc @returns / cast / .ts shapes from this issue.

  3. MartinJohns commented on Oct 29, 2026

    @MartinJohns
    Contributor

    Duplicate of #64974.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions