Automatico
Automatico

Reputation: 12916

TS compiler not complaining about possible undefined object destructuring?

We recently had an incident on our production system. There are plently of things we could, and should, have done to mitigate this, but never the less this happend.

Typings + example code

interface StatisticObject {
    Value: number,
    someData: string
}
function somePromise(): Promise<{data: {Statistics: StatisticObject[] | undefined}}> {
    return Promise.resolve({data: {Statistics: undefined}})
}

We had this code in production

somePromise()
.then(({ data: { Statistics } }) => {
    const [{ Value }] = Statistics || []

    return Value || 0
})

Example on ts playground (Error wording a bit different on ts playground)

This caused the error Cannot read property 'Value' of undefined, because the Statistics object was undefined, causing the || statement to trigger. This array was empty, thus the const [{Value}] destructuring failed.

I have two questions regarding this

  1. Is there a reason why the TS compiler did not catch this error? Could we have changed settings so it did catch this? It seems to me that this is a catchable issue, as inspecting the code it seems clearly faulty.
  2. I find it weird that TS/JS doesn't handle this destructuring case properly. I would expect Vaule to become undefined, not cause a crash. Could this be a bug?

If the Statistics || is removed, resulting in const [{ Value }] = [] then the results are as follows (ts playground example)

Tuple type '[]' of length '0' has no element at index '0'.(2493)
Property 'Value' does not exist on type 'undefined'.(2339)

This is the errors I would expect to see even with the Statistics || in front, because it is possibly undefined.

Finally; Is there a different pattern we can use to achieve the same without risking this problem agian in the future?

Upvotes: 1

Views: 323

Answers (1)

Sergiu Paraschiv
Sergiu Paraschiv

Reputation: 10153

At the moment TypeScript can't catch that. It will in 4.1.

Let's rewrite your code to this:

const x = Statistics || []
const y = x[0]        
return y.Value || 0

The inferred type of x is StatisticObject[]. What's the inferred type of y? It's StatisticObject but it actually should be StatisticObject | undefined because only at runtime could we know that the array is not empty.

The "failure" is in not checking that the array actually has at least one element. As I said, 4.1 is going to improve this by correctly inferring the type of y to StatisticObject | undefined and forcing you to check the length before accessing it.

Upvotes: 3

Related Questions