Skip to content

TS7031 false positive for nested object binding pattern with = {} default in an annotated parameter (regression from #64043) #64431

Description

🔎 Search Terms

TS7031, Binding element implicitly has an 'any' type, object binding pattern default, nested destructuring default, annotated parameter destructuring, padObjectLiteralType, 64043, 59920

🕗 Version & Regression Information

⏯ Playground Link

https://tsgo.sxzz.dev/#eNqtVGFr2zAQ/SuH9qEpOG6apB3zGKPbOuhgK7T9lgQq2+dERZY86Zw2hPz3neQkS9ttnwYBRb67d+89nbQWlcgmE1FLZVLyIhEnJ3CDjbMZ3N2+HYxOoZLaIzTWK1JLBGtAGmNJEpZg0IfF5g9YEOTKlMrMoZFE6IxPpyaizR16z2n5CmpVOOttRSd3qwZvC6caenM+HoxH0JuKC63m5iVaiZVsNXl4VLQA6Zxc+ak4juBdg9ZkQH5uod/35BTX9vvGXtaKwAUpLGyffPnUMDiT6ZEv4DwdpKOEBZUdQI6VdQgdo+MMjAV0zrqtlIuCWqlDKefKikXuU2Mc9v16w0EyPmOEWL61MoNPW02osUZDcJQfgaobrQpFegUL6ZkLHEmzOgJig7q2h7BnyWj4P2CVYfaVLBCujGEd66kBkB9Zclvn6N6HbX643RzWXLdBe6xRoZwTI0yXx5RPU7hVuQ6kYvfdKSZwn99DadGzt5TGnfKRVJwPWuBuvJQ1zBOfGusIqtYU4Uscgi1ybx17Z7CW8AFOE8g3vK43m6zjd9wRdEitMzCRnDDbExym8MOC33LcsgPJP63/0FZqa/B3w392yvdNPltDTnri8Qy6wugC/mzVUupwTHyq3CQ6sWAWzK8bPz7Zw+F6dlqx30VEemZ/d06TrcBX9EPBV+vqvYTJzrMZr5PZTklE/ptxYpZMBF8bayo1Tx+8NfxexNSpKGzdKI3uugkN+YKySyESYiTdHCl8mgr0TI7/J7tgbctWYxds+KFAt+Tdi/ANeqvbgNwl5q0pudlBXnfxQ5Rci/vP6L9HgKtgoG1exW1LXxTjBNBS+UNiJRZaujiHr8qYp9VL/MYWdPi7jJDA/rNVs0QUIhPs0JKX4WB4nr5Lh2PeP4osvqmbX5eN1GQ=

💻 Code

// Repro: TS7031 false positive on annotated nested object binding patterns.
// Regressed by microsoft/TypeScript#64043 ("Align object binding defaults with arrays").
//
// Run: tsgo --strict --noEmit repro.ts
//
// Expected (tsc 6.0.3, and tsgo before #64043): no errors.
// Actual (tsgo after #64043):
//   repro.ts(20,45): error TS7031: Binding element 'b' implicitly has an 'any' type.
//   repro.ts(25,32): error TS7031: Binding element 'b' implicitly has an 'any' type.

interface Inner {
  a?: number;
  b?: number;
}
interface Outer {
  inner?: Inner;
}

// 1. Sibling has a default, `b` does not. `b` is typed by the annotation.
export function withSibling({inner: {a = 1, b} = {}}: Outer) {
  return [a, b];
}

// 2. No sibling default at all.
export function alone({inner: {b} = {}}: Outer) {
  return b;
}

// Contrast: the array equivalent reports nothing, before or after #64043.
interface OuterArray {
  inner?: number[];
}
export function arrayForm({inner: [a = 1, b] = []}: OuterArray) {
  return [a, b];
}

🙁 Actual behavior

tsgo reports TS7031 on the two annotated parameters:

repro.ts(20,45): error TS7031: Binding element 'b' implicitly has an 'any' type.
repro.ts(25,32): error TS7031: Binding element 'b' implicitly has an 'any' type.

b is not actually any: the Outer annotation still types it. With --declaration, tsgo emits alone({ inner: { b } }: Outer): number | undefined, which contradicts the error.

🙂 Expected behavior

No errors, matching tsc 6.0.3 and tsgo before #64043.

The array analog (arrayForm, {inner: [a = 1, b] = []}: OuterArray) is still accepted, so after #64043 annotated object patterns are stricter than annotated array patterns rather than aligned with them.

Additional information about the issue

#64043 changed padObjectLiteralType to pad every missing non-rest binding element (previously only elements with an initializer) and to call it with reportErrors=true. That's right for the unannotated case from #59920 (({required, optional = false} = {}) => {}), which is the only case the new test covers. But when the enclosing parameter has a type annotation, the = {} initializer is checked against a contextual type that already supplies the property types, so reporting implicit-any on the padded elements is spurious.

The array path doesn't hit this because a contextually typed [] isn't inferred as a tuple, so padTupleType never runs.

A possible fix: only report implicit-any from padObjectLiteralType when the binding pattern has no declared or contextual type.

We hit this while moving a large codebase to tsgo; the pattern (nested destructuring with = {} defaults in a typed parameter) is fairly common there.

Activity

  1. Konan69 commented on Oct 7, 2026

    @Konan69

    This also hits parameters that are not annotated but are contextually typed, at the top level (not only nested patterns under an annotation). We found it while evaluating type checkers and build tooling for a large TypeScript monorepo (an AI product, 17 projects, Effect 4):

    // @strict: true
    type Options = (options?: { recursive?: boolean }) => void;
    export const f: Options = ({ recursive } = {}) => {
        const proof: string = recursive;
    };
    
    declare function take(cb: (options?: { deep?: boolean }) => void): void;
    take(({ deep } = {}) => void deep);
    
    interface Driver {
        rm(path: string, options?: { force?: boolean }): void;
    }
    export const driver: Driver = { rm: (path, { force } = {}) => void [path, force] };
    a.ts(3,30): error TS7031: Binding element 'recursive' implicitly has an 'any' type.
    a.ts(4,11): error TS2322: Type 'boolean | undefined' is not assignable to type 'string'.
    a.ts(8,9): error TS7031: Binding element 'deep' implicitly has an 'any' type.
    a.ts(13,46): error TS7031: Binding element 'force' implicitly has an 'any' type.
    

    The first two lines contradict each other. 6.0.3, 7.0.2 and 7.1.0-dev.20260922.1 accept this; 7.1.0-dev.20260923.1 is the first nightly that reports it, which matches #64043.

    I built #64440 locally and ran it on these cases: all four TS7031 errors are gone, and function plain({ a } = {}) {} (no type anywhere) still reports TS7031, so that fix covers the contextual case as well. These cases might be worth adding to its test.

    I also opened #64671 with a smaller alternative (keep the padding, only stop the report when the parameter is annotated or contextually typed), with these cases and the two from this issue in the test. Either one fixes it for us.

    Disclosure: I used an AI coding tool to bisect this and to prepare the reproduction and the patch; I have checked the results myself.

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

Metadata

Metadata

Labels

BugA bug in TypeScript

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions