Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why TypeScript cannot resolve assignment in object destructuring?

Tags:

typescript

Let's have a following code:

interface Foo {
    a?: number
    b?: number
}

function foo(options?: Foo) {
    const {
        a, // <-- error here
        b = a
    } = (options ?? {})

    return [a, b]
}

Why this code fails with:

'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

Doesn't a have type number | undefined?

However, if I rewrite the foo function to:

function foo(options?: Foo) {
    const f = options ?? {}
    
    const {
        a,
        b = a
    } = f

    return [a, b]
}

No error is emitted.

Why? Is it a bug?

TypeScript playground

like image 797
TN. Avatar asked Jun 29 '26 20:06

TN.


1 Answers

This was a bug in TypeScript, as described in microsoft/TypeScript#49989. A pull request at microsoft/TypeScript#56753 fixes it, so as of TS5.4 this particular problem should no longer be an issue:

Playground link, TS version 5.4.0-dev.20240111


In general, however, TypeScript's type inference algorithm is limited in its ability to avoid circularities. See microsoft/TypeScript#45213 for a similar situation that runs into this. If, when analyzing const { a, b = a } = options ?? {}, the compiler decides to, for whatever reason, defer the analysis of options ?? {} until after it knows the context in which it is assigned, then const { a, b = a } will be seen as circular since the type of a depends on the type of {a, b} which depends on the type of b which depends on the type of a. Of course a human being would never get caught in such a situation, but the type checker doesn't have the luxury of seeing "the big picture".

While the particular problem you found is indeed considered a bug in TypeScript and had a straightforward fix, there are many similar scenarios classified as a design limitation of the language, because fixing it would require a massive refactoring. As mentioned in this comment on microsoft/TypeScript#45213:

It's instructive to look at the call stack at the point that we log the circularity error and see how every caller in that path (of which the incoming call stack is could be practically any function in the checker) would have to be basically completely rewritten to handle the answer of "ask me again later".

We also need to be able to detect circularities instead of looping forever. [...] then any sort of work-deferring mechanism needs to be able to detect when it's looping forever so that it doesn't attempt to infer an infinite sequence [...].

The relevant codepaths in the checker here are not especially complicated (relatively speaking) and I'd encourage you to try a PR to understand the problem more deeply. Not to be glib, but if this were low-hanging fruit, we would have done it already 😉. Again, it's not impossible, but it's not at all a straightforward fix - we'd have to rewrite very large portions of the entire checker.

like image 65
jcalz Avatar answered Jul 01 '26 09:07

jcalz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!