Skip to content

TS7031 on a contextually typed parameter with a destructured = {} default #16

Open

Description

@Konan69

tsc-rs 0.1.0 reports TS7031 for a destructured parameter with a = {} default, even when the function is contextually typed and the binding element has a real type.

Repro

// tsconfig: { "compilerOptions": { "strict": true, "noEmit": true } }
type Mk = (options?: { recursive?: boolean }) => void

export const mk: Mk = ({ recursive } = {}) => {
  const proof: string = recursive
  void proof
}
a.ts(3,26): error TS7031: Binding element 'recursive' implicitly has an 'any' type.
a.ts(4,9): error TS2322: Type 'boolean | undefined' is not assignable to type 'string'.

The two errors contradict each other: the second one shows that recursive is boolean | undefined.

The same happens for a callback argument and for a method of an object literal typed by an interface:

declare function take(cb: (options?: { deep?: boolean }) => void): void
take(({ deep } = {}) => void deep) // TS7031

interface Driver { rm(path: string, options?: { force?: boolean }): void }
export const driver: Driver = { rm: (path, { force } = {}) => void [path, force] } // TS7031

An annotated parameter is fine (({ a }: { a?: number } = {})), and a parameter with no type anywhere (function f({ a } = {}) {}) should keep its error.

Where it comes from

This is upstream behaviour that the port copies. typescript@6.0.3, typescript@7.0.2 and typescript@7.1.0-dev.20260922.1 accept the code. typescript@7.1.0-dev.20260923.1 and later report TS7031. The change is microsoft/TypeScript#64043 ("Align object binding defaults with arrays"): padObjectLiteralType now pads every binding element missing from the default and calls getTypeFromBindingElement with reportErrors set, without looking at the contextual type of the parameter.

In the port that is pad_object_literal_type in crates/ts_goport/src/checker/checker_p19.rs.

Effect on a real project

On our monorepo (17 projects, Effect 4) tsc-rs and tsc 7.0.2 with @effect/tsgo give the same 1,677 diagnostics, and these are the only 3 lines that differ. They come from an object that implements an SDK interface:

mkdir: (path, { recursive } = {}) => ...
rm: (path, { recursive, force } = {}) => ...

I understand the port follows Go on purpose. I am opening this so the difference from 7.0.x is on record, and I have a small fix in a pull request in case you want it before upstream changes.

Activity

  1. Konan69 commented on Oct 7, 2026

    @Konan69
    Author

    Upstream already tracks this: microsoft/TypeScript#64431 (labelled Bug), with a draft fix in microsoft/TypeScript#64440. I built that draft and it clears the contextually typed cases above as well, so the port will pick the fix up when it moves its pin. Happy for #17 to be closed if you would rather wait for that.

  2. added
    upstream issueAlso happens in tsgo at the pinned upstream revision; fixed by a later pin bump
    on Oct 7, 2026
  3. t3dotgg commented on Oct 7, 2026

    @t3dotgg
    Member

    Thanks for the clear report and the upstream links, @Konan69.

    tsc-rs follows microsoft/TypeScript at commit 673a5f17d713 (2026-09-29), and its output is meant to be byte-equal to tsc at that commit. To compare, use typescript@7.1.0-dev.20260929.1. All three of your cases give the same TS7031 there, in the newest nightly (7.1.0-dev.20261007.1) and in a Go build of upstream main from today. It came in with microsoft/TypeScript#64043, as you found.

    We built both upstream fixes, the draft microsoft/TypeScript#64440 and your microsoft/TypeScript#64671. Both clear your three cases and the repros in #14.

    We will keep this issue open to track microsoft/TypeScript#64431, and close #14 as a duplicate of it. We do not patch upstream bugs ahead of the pin, so tsc-rs will get the fix with the pin bump after upstream merges one.

    Until then, a typed default avoids the error, in tsc-rs and in upstream:

    export const mk: Mk = ({ recursive }: { recursive?: boolean } = {}) => { /* ... */ }
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

    upstream issueAlso happens in tsgo at the pinned upstream revision; fixed by a later pin bump

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions