Videos
Jumping into a new code base and it seems like optional chaining is used EVERYWHERE.
data?.recipes?.items
label?.title?.toUpperCase();
etc.
It almost seems like any time there is chaining on an object, it always includes the ?.
Would you consider this an anti-pattern? Do you see any issues with this or concerns?
Hi everyone!
I love optional chaining, i really do, but there are some cases where using this syntax damages the readability of the code. One of those cases is the following
function optionalFunction(){
console.log("works");
}
// optionalFunction = undefined;
optionalFunction?.(); While i understand this approach, i find it optionalFunction?.() harder to read as opposed to this
function optionalFunction(){
console.log("works");
}
// optionalFunction = undefined;
if(optionalFunction != undefined){
optionalFunction();
} I think i'd rather have a more readable and stronger check than ES6 magic when checking if an optional function is defined.
I believe that optional chaining fixes the problem of checking if a property of an object exists, and if exists, then get the value or keep going deeper in the object structure. But this syntax just looks weird for calling functions, it looks a lot like those "one line cleverness" code that sometimes people encounter.
What are your thoughts about this?
This is currently a Stage 4 proposal you can check on the progress of it here:
https://github.com/tc39/proposal-optional-chaining
You can use the babel plugin today:
https://www.npmjs.com/package/babel-plugin-transform-optional-chaining
Update 11th January 2020: Babel now supports optional chaining by default https://babeljs.io/blog/2020/01/11/7.8.0
The Optional Chaining operator is spelled ?.. It may appear in three positions:
obj?.prop // optional static property access
obj?.[expr] // optional dynamic property access
func?.(...args) // optional function or method call
Notes:
In order to allow foo?.3:0 to be parsed as foo ? .3 : 0 (as required for backward compatibility), a simple lookahead is added at the level of the lexical grammar, so that the sequence of characters ?. is not interpreted as a single token in that situation (the ?. token must not be immediately followed by a decimal digit).
Also worth checking out:
https://github.com/tc39/proposal-nullish-coalescing
https://github.com/babel/babel/tree/master/packages/babel-plugin-proposal-nullish-coalescing-operator
In plain JavaScript you have to do type checks or structure your code so that you know an object will exist.
CoffeeScript, a language that compiles down to JavaScript, provides an existential operator ?. for safe chaining if you're willing to consider a preprocessed language.
There's another discussion here about why you can't reproduce this behavior in JS.
There is also a discussion on the ESDiscuss forums about adding an existential operator to a future version of JavaScript. It doesn't seem very far along though, certainly nowhere close to practical use. More of an idea at this point.
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
);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
And with function calls:
const obj = {
fn2: () => console.log('fn2 running')
};
obj.fn1?.();
obj.fn2?.();
Run code snippetEdit code snippet Hide Results Copy to answer Expand
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 .
Its called "optional chaining" operator. It moves from left to right. if it sees ?. it checks the left value if its undefined or null. if yes, it returns undefined and stops moving to the right. that means if b is already undefined it wont check the values on the right side
In the example below, obj has only one key a.
const obj = {a: 1};
console.log(obj?.b);
In the above code snippet, there is no property b in object because of which it is printing undefined.
console.log(obj.b.c.d?.e);
console.log(obj.b?.c.d?.e);
Even in the code above, the same thing is happening where the object is not having key b hence it is printing undefined. So if you want to see that obj.b.c is undefined then the object should look the one below:
obj: {
b: '123'
}