334

I'm trying to use optional chaining with an array instead of an object but not sure how to do that:

Here's what I'm trying to do myArray.filter(x => x.testKey === myTestKey)?[0]. Also trying similar thing with a function:

let x = {a: () => {}, b: null}
console.log(x?b());

But it's giving a similar error - how can I use optional chaining with an array or a function?

3
  • 2
    What input data do you have, and what results do you want? Commented Jan 7, 2020 at 7:06
  • 3
    That's a generic question shouldn't depend on input and output ? does a null check to prevent long use of && && chains. Commented Jan 7, 2020 at 7:08
  • 1
    short answer, YES, myArr?.[0] Commented Oct 19, 2024 at 13:07

5 Answers 5

576

You need to put a . after the ? to use optional chaining:

myArray.filter(x => x.testKey === myTestKey)?.[0]

Playground link

Using just the ? alone makes the compiler think you're trying to use the conditional operator (and then it throws an error since it doesn't see a : later)

Optional chaining isn't just a TypeScript thing - it is a finished proposal in plain JavaScript too.

It can be used with bracket notation like above, but it can also be used with dot notation property access:

const obj = {
  prop2: {
    nested2: 'val2'
  }
};

console.log(
  obj.prop1?.nested1,
  obj.prop2?.nested2
);

And with function calls:

const obj = {
  fn2: () => console.log('fn2 running')
};

obj.fn1?.();
obj.fn2?.();

1
  • 9
    This also works for Javascript (since 2020 I think). Similarly, to call a function if it isn't null or undefined, you can also use foo?.()
    – Luke Vo
    Commented Aug 16, 2020 at 19:22
51

Just found it after a little searching on the what's new page on official documentation

The right way to do it with array is to add . after ?

so it'll be like

myArray.filter(x => x.testKey === myTestKey)?.[0] // in case of object
x?.() // in case of function

I'll like to throw some more light on what exactly happens with my above question case.

myArray.filter(x => x.testKey === myTestKey)?[0]

Transpiles to

const result = myArray.filter(x => x.testKey === myTestKey) ? [0] : ;

Due to which it throws the error since there's something missing after : and you probably don't want your code to be transpilled to this.

Thanks to Certain Performance's answer I learned new things about typescript especially the tool https://www.typescriptlang.org/play/index.html .

0
33

ECMA 262 (2020) which I am testing on Edge Chromium 84 can execute the Optional Chaining operator without TypeScript transpiler:

// All result are undefined
const a = {};

console.log(a?.b);
console.log(a?.["b-foo-1"]);
console.log(a?.b?.());

// Note that the following statements throw exceptions:
a?.(); // TypeError: a is not a function
a?.b(); // TypeError: a?.b is not a function

CanIUse: Chrome 80+, Firefox 74+

3
  • This is THE solution - why are others using it withe looping methods ? - I got it solved using obj?.people?.p15?.results?.science
    – anjanesh
    Commented Sep 7, 2022 at 4:53
  • @anjanesh they are answering the specific question by OP. No other answer is using loop btw unless you count internal loop filter method.
    – Luke Vo
    Commented Sep 7, 2022 at 5:06
  • 1
    Wouldn't filter take more time and optional chaining ?
    – anjanesh
    Commented Sep 7, 2022 at 5:51
19

It's not necessary that the function is inside the object, you can run a function using optional chaining also like this:

someFunction?.();

If someFunction exists it will run, otherwise it will skip the execution and it will not error.

This technique actually is very useful especially if you work with reusable components and some components might not have this function.

2
  • 2
    Docs
    – shmuels
    Commented Aug 31, 2022 at 19:01
  • Came here for this. In my head the operator was just adding the ? to the . acessor already so I naively assumed the correct syntax was someFunction?(). I must admit, the ?.() does look a little weird, as if you are "accessing" the function instead of invoking it. Commented Sep 8, 2023 at 7:48
2

Well, even though we figured out the correct syntax, the code doesn't make much sense to me.

The optional chaining in the code above is making sure, that the result of myArray.filter(x => x.testKey === myTestKey) is not null and not undefined (you can have a look at the TS output). But it is not possible anyway, because the result of the filter method is always an array. Since JavaScript doesn't throw "Array bounds exceeded", you are always safe when you try to access any index - you will get undefined if this element doesn't exist.

More example to make it clear:

const myArray: string[] = undefined
console.log(myArray.filter(x => x)?.[0]) //throws Cannot read property 'filter' of undefined
//in this example the optional chaining protects us from undefined array
const myArray: string[] = undefined
console.log(myArray?.filter(x => x)[0]) //outputs "undefined"
0

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