How can I add a property to an object conditionally - javascript

I have read this In Javascript, how to conditionally add a member to an object? but in my case, it doesn't work.
I have the following situation:
angular.forEach(vm.someData.entities, entity => {
...(entity.type === "section" && { entity.styleFormats = entity[`${vm.activeTab}StyleFormats`]});
entity.content = entity[`${vm.activeTab}Content`];
entity.stateClasses = determinateStateClasses(false);
entity.isNew = false;
});
The spread operator gives me a parsing error.

Don't use the spread operator here. That operator is used when constructing an object literal, which isn't what you're doing here. You're simply writing a line of code in a function.
You may be able to use && short circuiting to perform the intended operation:
entity.type === "section" && entity.styleFormats = entity[`${vm.activeTab}StyleFormats`];
But this is a little unclear. Normally this kind of short-circuiting is used when resolving to a value, not when performing an operation. Think of this as structurally like the difference between using the conditional operator (?:) vs. using an if block. The former is not a drop-in substitute for the latter, they have their own uses.
In this case what you're looking to do is conditionally perform an operation within your function. That's exactly what an if block is for:
if (entity.type === "section") {
entity.styleFormats = entity[`${vm.activeTab}StyleFormats`];
}
Basically, don't try to write the cleverest code possible, write the clearest code possible. There's no reason to avoid using an if block.

Related

What does the myObj.param?.otherParam operator do and how is it used?

I was reading the fundamentals guide for React Navigation and in the section for passing parameters to routes I came across this bit of code that I've never seen before.
if (route.params?.post) {
// Do something
}
I've never seen the ? operator used that way, I've only used the ternary operator. When searching the only other thing I found is the nullish assignment operator ??=.
I fiddled with it in the console and it seems to check if param exists so that if it doesn't exist it doesn't error when asking for .post
My first thought was that is a ternary operator without the second argument, but the third argument appears to be required.
So my question is, in the above code block, what is the ? doing, what is that called, and how/when is it used?
Thanks
It's called optional chaining. You can use it to check if the preceding variable is null, so that you could spare yourself checking for null/undefined properties.
// want to access blub.test.smth
if(blub && blub.test) {
// possibly access blub.test.smth
const value = blub.test.smth;
}
vs
const value = blub?.test?.smth

is there any equivalent for PHP's "??" operation in javascript? [duplicate]

This question already has answers here:
Null-safe property access (and conditional assignment) in ES6/2015
(11 answers)
Closed 2 years ago.
I've been programming a lot in Swift recently. Today I did some work in JavaScipt when question popped up to me:
Is there something similar to optional chaining in JavaScript? A way to prevent undefined is not an object without any variables?
Example:
function test(){
if(new Date() % 2){
return {value: function(){/*code*/}};
}
}
test().value();
will fail half of time because sometimes test returns undefined.
The only solution I can think of is a function:
function oc(object, key){
if(object){
return object[key]();
}
}
oc(test(), 'value');
I would like to be able to do something like:
test()?.value()
The part after the question mark is only executed if test returned an object.
But this is not very elegeant. Is there something better? A magic combination of operators?
Edit I know I could rewrite test to return something. But I'm wondering if there's something like optional chaining. I'm not interested in a particular solution to the above example. Something that I also can use if have no control over the function returning undefined.
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.
Optional chaining has landed in JS. We can use optional chaining via the ?. operator in object property access. It allows us to try accessing properties of objects which might not exists (i.e. are undefined) without throwing an error.
Here is a code example:
const obj = {
foo: {
bar: 1
}
};
// If we try to access property which doesn't exists
// it just returns undefined
console.log(obj.baz);
try {
// Now we try to access a property of undefined which throws an error
obj.baz.foz;
} catch (e) {
console.dir(e.message);
}
// Now we use the optional chaining operator ?.
// We get undefined instead of an error
console.log(obj.baz?.foz);
console.log(obj.foo?.bar);
You can use
test() && test().value();
or
var testResult = test();
testResult && testResult.value();
If you ask me this is most similar to Swift's optional chaining.
var Obj = {Prop: {name: 'peter'}}
console.log(Obj.Prop.name)
console.log(Obj?.Prop?.name)
In the first sentence, you're just accessing object properties. The problem with that is that if you find Prop to be something other than an object, it will throw an exception. That's the reason of the optional chainig operator.
Lets say you try to do Obj.Prop2.name.
You'll get Uncaught TypeError: Cannot read property 'name' of undefined
if Instead you did Obj.Prop2?.name, You'll only receive undefined as a value, instead of an exception.
This is particularly useful when accessing deeply nested properties.
WARNING: This is a relatively new JS feature that's not yet implemented in all browsers, so be careful while using it for production applications.
Optional Chaining is finally in the JavaScript standard!
Here are a few examples:
// properties
foo?.bar
foo?.bar()
foo?.bar.baz()
foo?.bar?.baz()
// indexing
foo?.[0]
foo?.['bar']
// check if a function is defined before invoking
foo?.()
foo.bar?.()
foo?.bar?.()
And this is way better than what most people use for manually checking for nulls
Instead of evaluating
foo?.bar
to this little code snippet we are all used to writing
foo ? foo.bar : null
it actually evaluates to
foo == null ? undefined : foo.bar
which works for all the falsey values like an empty string, 0 or false.
Unrelated to the question, but you might also be interested in the ?? operator.
It has a similar purpose as || except it only checks for null or undefined.
For example:
foo ?? bar
would be the same as:
foo != null ? foo : bar
This is a very new feature, so even thought a lot of users already use a browser that supports this you will still want to use a tool to convert it to an older version of javascript.
What about returning a noop function that does nothing when the condition isn't met?
function test(){
if(new Date() % 2){
return {value: function(){/*code*/}};
}
return {value: function(){ /* just return a type consistent with the function above */ }
}
You can always return this; if test is a object method.
But why do you want to prevent such errors by creating mock functions?

Unconditional use of conditional expression for default assignment

I'm getting the following linting error: Unconditional use of conditional expression for default assignment
What is wrong with the below?
(myOverride) ? myOverride : MAGIC_HOST,
Where if myOverride is defined I want to use myOverride, if it is not defined I want to use the env var MAGIC_HOST.
Apparently you're using ESLint (as that error is an ESLint) error. It's because of the no-unneeded-ternary rule which is meant to flag up unnecessary use of the conditional operator (they call it the "ternary")¹. From the linked docs:
Another common mistake is using a single variable as both the conditional test and the consequent. In such cases, the logical OR can be used to provide the same functionality. Here is an example:
// Bad
var foo = bar ? bar : 1;
// Good
var foo = bar || 1;
So the rule is telling you to use myOverride || MAGIC_HOST instead.
You don't have to, the code you've shown isn't wrong. It's just it doesn't pass that ESLint rule.
¹ "they call it the 'ternary'" - The conditional operator is a ternary operator (an operator accepting three operands, just like * is a binary operator — an operator accepting two operands). And it is, for now, the only ternary operator JavaScript has. But that doesn't necessarily always have to be true as the language evolves. It's correctly called the conditional operator.
It is not inherently wrong, but it is better written as:
myOverride || MAGIC_HOST
as explained here.

Is it appropriate to use eval() on JavaScript expressions?

I'm currently reading up on "Content-based publish/subscribe" design patterns and in one of the implementations it suggests subscribing to an event using expressions in string form ie. eventMiddleware.on("foo===bar && bool",callback).
However I'm not quite sure how to handle the string expression middleware side.
I'm constantly reading about how eval is "evil" and that I shouldn't use it, but is it appropriate in this case? Is there a better method? or should I just simply stay away from registering events with expressions entirely?
If "foo===bar && bool" is produced server-side just replace it with function (x) { return x === bar && whatever-you-need; }. Code your way around so that you evaluate the function by just calling it. You won't need eval at all if you can get this far.
The predicate is a guard. Say you define it this way:
function on(guard, callback)
{
}
Then, in your pattern, the message dispatcher should just do this:
if (tyepof(guard) === "FUNCTION" && guard(arg))
{
callback(arg);
}
I hope this is comprehensible.

Using try-catch to retrieve the value of nested property. Is it an valid approach?

We have an object (referenced by data) and we want to retrieve the value of a nested property. Ideally, we would like to do it like so:
value = data.category3.section2.article4.title;
We cannot do this like so, because the above line throws a reference error if any of the mediate objects (category3, section2, or article4) are not defined (at the corresponding positions) inside the data object.
Now, to nullify any potential reference errors that might be thrown, we could just place the above line inside a try-catch statement:
try {
value = data.category3.section2.article4.title;
} catch (err ) {}
This works! However, I am not confident that relying on try-catch in such a way is a good practice. The alternative solution would be to manually traverse to the desired property value. I have written a compact utility function that accomplishes that:
function get( val, names ) {
names = names.split( '.' );
while ( val && names.length ) { val = val[ names.shift() ]; }
return val;
}
Now we can get the property value like so
value = get( data, 'category3.section2.article4.title' );
So, my question is:
Is the try-catch approach a valid solution? Or are there valid reasons why it should be avoided?
Btw, the try-catch approach is heavily biased in this thread: What's the simplest approach to check existence of deeply-nested object property in JavaScript?
Why not:
var value = data &&
data.category3 &&
data.category3.section2 &&
data.category3.section2.article4 &&
data.category3.section2.article4.title;
That is safe (if any of the objects in the traversal chain are not set, value will be null). That is a little neater than a bunch of if blocks, and avoids (?mis)using exceptions.
Another use of that method to provide a default value on failure:
var value = data &&
data.category3 &&
data.category3.section2 &&
data.category3.section2.article4 &&
data.category3.section2.article4.title || 'default value';
Both are fine. The only major differences between them I can think of are that
The try-catch may cause a debugger to unecessarily halt too often if you tell it to stop on all exceptions.
This is relevant you need to debug code that is swallowing exceptions. For example, some promise libraries wrap all callbacks in a try-catch block.
The string splitting version can't easily cope with properties that contain a dot in them
var x = {'.': {a: 17}};
try{ obj['.'].a }catch(e){}
get(/*???*/)
If you want something robust that avoids both pitfalls I would suggest a function that can (at least optionally) directly receive a list of properties.
get(val, ['prop1', 0, '.', 'category2']);
I think the differences here are going to be mostly contextual - it depends on the data you're trying to access and what you want to do with it.
For example, the second function will return equivalent undefined values for a variety of circumstances, including both data.category3 === undefined and data.category3.section2.article4.title === undefined. Using try/catch here tells you that you have an actual traversal error, rather than a property that hasn't been set, which you might want to handle differently.
Abusing try catch like this is a dirty hack.
Try catch is there to catch exceptions you throw. Exceptions are used for exceptional cases.
In this case both cases are wrong. You should never have to traverse data.category3.section2.article4.title; where every step can fail.
You should simply be able to assert that if data has a category then it should have a section, article and title.
I say refactor the code so you don't have multiple levels that can fail.
I have seen the answers here and I think that the traversing is your best move, but it looks quite bothersome. You can make a function that traverses it for you or you can use the almighty brototype library found at: https://github.com/letsgetrandy/brototype
This way you can do something like this:
if (Bro(data).doYouEven('category3.section2.article4.title')) {
value = data.category3.section2.article4.title;
}
or you can use a callback:
Bro(app).iDontAlways('category3.section2.article4.title')
.butWhenIdo(function(title){
value = title;
});
I think everyone should check this amazing library out, and code with great bro-ness.
If you dislike the brototype, you can indeed use your own get function.

Categories

Resources