1
\$\begingroup\$

So I'm parsing an object using Typescript. I am pushing a value from within the object into an array of strings.

Here's the code:

if (data.InstanceStatuses != undefined)
            if (data.InstanceStatuses[0].InstanceId != undefined)
                instanceIds.push(data.InstanceStatuses[0].InstanceId as string)

I feel there must be a better way to deal with the fact that both data.InstanceStatuses and data.instanceStatuses[0].InstanceId can be of type string || undefined, however I am not sure how to get it down to a simpler statement with less IF blocks.

Any ideas?

\$\endgroup\$
1
\$\begingroup\$

Typescript has supported optional chaining for a while. To do this concisely and avoid the somewhat inelegant as string type assertion, extract the possibly nested value into a variable, then check its typeof against string:

const instanceId = data.InstanceStatuses?.[0].InstanceId;
if (typeof instanceId === 'string') {
  instanceIds.push(instanceId);
}

This way, instanceId will be either the nested value, or, if InstanceStatuses doesn't exist, it'll be undefined. So, it'll only push to the array if the nested value exists and the value is a string.

\$\endgroup\$
1
\$\begingroup\$

An alternative to optional chaining is to make something like this:

const optionalChaining = (obj: Object, ...props: string[]) =>
   props.reduce((o: any, prop: string) => (o || {})[prop] , obj);

That is:

  • Try to access the prop in every iteration, even if it returns undefined
  • In case the previous iteration returned undefined, just create an object on the fly to not throw an Error because you're trying to access props of undefined.

See how Array.prototype.reduce() works, but I like to think that you can use when you want transform an array in one value.

In this case, you want to get an array of props, and get the "chained value" of them or undefined if there's not a valid prop among them.

You could use it like:

const optionalChaining = (obj: Object, ...props: string[]) =>
    props.reduce((o: any, prop: string) => (o || {})[prop] , obj);

const foo = {
  data: {
    InstanceStatuses: [
      { InstanceId: 'bar' }
    ]
  }
};

console.log(
  optionalChaining(
    foo,
    'data',
    'InstanceStatuses',
    '0',
    'InstanceId'
  )
);

console.log(
    optionalChaining(
        foo,
        'data',
        'InstanceStatuses',
        '1',
        'InstanceId'
    )
);

The advantage is that Array.prototype.reduce has a good browser compatibility, in other words, is a better cross-browser solution, but I would prefer optional chaining syntax when it will has a good major browsers support.

By other hand, maybe Babel could be useful if browser compatibility matters for you.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.