0

Javascript Ternary Condition

I have javascript with this condition, what is this?

var y = (typeof x !== undefined) ? x || 0 : 1;
mplungjan
  • 169,008
  • 28
  • 173
  • 236

4 Answers4

3

This (typeof x !== undefined) ? x || 0 : 1; is going to return always true because the typeof operator will return a string.

That condition should compare a string as follow:

(typeof x !== 'undefined') ? x || 0 : 1;

var x;
var str = typeof x !== 'undefined' ? x || 0 : 1;

console.log(str);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Conditional (ternary) Operator explanation:

                         +--- When condition is true
                         |
                         |        +--- When condition is false
                         |        |
                         |        |
                         v        v
typeof x !== 'undefined' ? x || 0 : 1;
  ^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |                             |
  |                             +---- The condition should be:
  |                                   (x === undefined ? 1 : x || 0)
  |
  +--- Checks for the type of x

Code refactored

var x = 4;
var str = x === undefined ? 1 : x || 0;
console.log(str);// should return 4

var y;
str = y === undefined ? 1 : y || 0;
console.log(str);// should return 1

y = null;
str = y === undefined ? 1 : y || 0;
console.log(str);// should return 0

y = 5;
str = y === undefined ? 1 : y || 0;
console.log(str);// should return 5

y = 5-"5";
str = y === undefined ? 1 : y || 0;
console.log(str); // should return 0
.as-console-wrapper { max-height: 100% !important; top: 0; }

Docs

Community
  • 1
  • 1
Ele
  • 33,468
  • 7
  • 37
  • 75
  • Why correct the bad code that led to the bug? Just do a proper comparison to `undefined` so you're not perpetuating bad coding. –  Mar 07 '18 at 16:38
  • 1
    That's a good catch, but you still need to explain the reset of the ternary, which was really what the question was about. – Scott Marcus Mar 07 '18 at 16:39
  • The question is asking specifically about the condition, which is what Ele is answering. –  Mar 07 '18 at 16:41
  • *"This OR is unnecessary because x will be a value different than undefined, so the condition should be (typeof x !== 'undefined' ? x : 1) even better, switch the condition to: (typeof x === 'undefined' ? 1 : x)"* That's incorrect. There are multiple values that could trigger the right hand side of the `||`. –  Mar 07 '18 at 16:54
  • @doodlemeister can you explain which ones? – Ele Mar 07 '18 at 16:56
  • All "falsey" values. `undefined`, `NaN`, `false`, `""`, `0`, `null`. –  Mar 07 '18 at 16:57
  • ...the continued use of `typeof x === "undefined"` in the reworked code is unfortunate. –  Mar 07 '18 at 17:02
  • @doodlemeister post an answer with your recommendations. – Ele Mar 07 '18 at 17:04
  • @Ele: No, people rarely want to hear information that makes them think. –  Mar 07 '18 at 17:05
1

First, there is an error with the condition:

(typeof x !== undefined)

because you are comparing a type against a value.

typeof always returns a string, while undefined is a value. So, whatever type x is, it will be returned as as string. Even if it's value is undefined, "undefined" (notice the quotes?) will be returned as its type and since the string "undefined" has a typeof === "string", the condition will actually branch into the true section, even when x actually is undefined.

So, it needs to be: (typeof x !== "undefined").

Or, you could test the value of x against the value undefined:

(x !== undefined)

But, you can't mix and match values and types.


Now, assuming we correct that, the next part (the true branch):

x || 0

Simply returns x, as long as it is not "falsy" (that is, any value that would convert to the Boolean false). 0, false, NaN, undefined, "" or null are all falsy. So, if x is not falsy, x is returned. If x is falsy, then 0 is returned. This is a way to provide a default return value in case the first value doesn't exist. But, the logic is a bit off here, because if the code has entered the true branch, it's because x is not undefined, which means it's "truthy". And, if it's truthy, then we can safely just return x. So, it really should just be:

x

Finally, the last part (the false branch)

1

Is what will be returned if the original condition is false. In this case, if x is undefined.

So, the code has flaws in it and really should be:

(typeof x  !== "undefined") ? x : 1

EXTRA CREDIT:

In reality, any expression you place into the condition of an if statement is going to be converted to a Boolean for the if to do its job. If all you need to know is if x is not a "falsy" value, then all you need to do is write:

x ? x : 1;

The x will be converted to a Boolean. If it's true (truthy), then x is returned. If it's false (falsy), then 1 is returned.

Examples:

function testX(x){
  return x ? x : 1;
}

// Truthy:
console.log(testX(10));           // 10
console.log(testX({}));           // {}
console.log(testX(true));         // true
console.log(testX("something"));  // "something"

// Falsy:
console.log(testX(""));     // 1
console.log(testX());       // 1
console.log(testX(4-"Z"));  // 1 because 4-"Z" == NaN
console.log(testX(false));  // 1
console.log(testX(0));      // 1
console.log(testX(null));   // 1
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • I believe that `(false || 0) === 0`, so that's another value that is not undefined or null – Logar Mar 07 '18 at 16:49
  • "But, the logic is a bit off here, because if the code has entered the true branch, it's because x is not undefined, which means it's truthy." This part is wrong. the initial condition can indeed be improved but in case of `x === NaN`, returning `x || 0` is useful – Logar Mar 07 '18 at 17:25
  • @Logar I don't see how that is true. See my examples at the end of my answer. There is a `NaN` test in there and the code works as it should. If `x === NaN`, why would you want to return `x`? – Scott Marcus Mar 07 '18 at 17:28
  • This `testX(4-"Z")` should return `0` according to the OP's approach. – Ele Mar 07 '18 at 17:38
  • @Logar is correct. When entering the "true branch" of the conditional operator, the only thing we know about `x` is that it is not `undefined`, and as you state, there are multiple "falsey" values beyond `undefined`. So you're contradicting yourself within the same paragraph. –  Mar 07 '18 at 17:51
  • ...and the "extra credit" `x ? x : 1;` could be better written as `x || 1;`, but I think it's pretty clear that that's not the OP's intent, since they're substituting `0` for a reason. As written, with the `undefined` check corrected, any falsey value other than `undefined` should assign `0` instead of `1`. –  Mar 07 '18 at 17:55
0

The condition

(typeof x !== undefined)

asks if x is not of type undefined. Or, if x is defined. This will include any value or even null.

...? x || 0

If so, the expression evaluates to this. Which is the value of x in most cases or 0 if x is anything evaluated to be boolean false, e.g., null, false, etc.

... : 1;

Otherwise (i.e. case when x is undefined), evaluates to 1.

Typing in Javascript is complicated (in my opinion), sometimes it is not easy to remember what it is when you're comparing mixed type stuff, see https://dorey.github.io/JavaScript-Equality-Table/ for a summary matrix.

adrtam
  • 6,991
  • 2
  • 12
  • 27
0

Admitting that you have a left-hand operand, it does the same thing as :

var y;
if(typeof x !== undefined) {
    if(x)
        y = x;
    else
        y = 0;
}
else
    y = 1;
Logar
  • 1,248
  • 9
  • 17
  • `undefined != "undefined"` – Scott Marcus Mar 07 '18 at 16:55
  • @ScottMarcus: Logar didn't say it was. This answer accurately represents what happens and says nothing of what the result will be. –  Mar 07 '18 at 16:56
  • Yes, this has been stated in the 2 other answers and they are more detailed than mine. I was just strictly answering OP's question – Logar Mar 07 '18 at 16:56
  • The answer should address the mistake in the original code. That's just how good answers work on SO. – Scott Marcus Mar 07 '18 at 16:59
  • @ScottMarcus I don't want to change OP's original code in my example, nor do I want to add the precision as I didn't catch it in the first place and your answer as well as Ele's (which are more voted and more precise anyway) already took care of that. Imho this downvote is not deserved, but I can live with it – Logar Mar 07 '18 at 17:04
  • 1
    I'm just trying to help you understand how [good answers](https://stackoverflow.com/help/how-to-answer) work on SO. Each answer should be complete. What happens if another answer which states something that you didn't gets removed from SO in the future? You have to remember that SO is not just a place for one person to get their question answered, it's a knowledge base for people with similar questions in the future. Each answer should be as complete as possible. The down vote is warranted because it's an incentive for you to write a better answer. – Scott Marcus Mar 07 '18 at 17:12