Skip to main content

Is a JS Boolean having custom properties a bad practice? [Resolved]

In JS you can return a Boolean having custom properties. Eg. when Modernizr tests for video support it returns true or false but the returned Boolean (Bool is first class object in JS) has properties specifying what formats are supported. At first it surprised me a bit but then I began to like the idea and started to wonder why it seems to be used rather sparingly?

It looks like an elegant way of dealing with all those scenarios where you basically want to know if something is true or false but you may be interested in some additional info that you can define without defining a custom return object or using a callback function prepared to accept more parameters. This way you retain a very universal function signature without compromising capacity for returning more complex data.

There are 3 arguments against it that I can imagine:

  1. It's a bit uncommon/unexpected when it's probably better for any interface to be clear and not tricky.
  2. This may be a straw man argument, but with it being a bit of an edge case I can imagine it quietly backfires in some JS optimizer, uglifier, VM or after a minor clean up language specification change etc.
  3. There is better - concise, clear and common - way of doing exactly the same.

So my question is are there any strong reasons to avoid using Booleans with additional properties? Are they a trick or a treat?


Plot twists warning.

Above is the original question in full glory. As Matthew Crumley and senevoldsen both pointed it is based on a false (falsy?) premise. In fine JS tradition what Modernizr does is a language trick and a dirty one. It boils down to JS having a primitive bool which if set to false will remain false even after TRYING to add props (which fails silently) and a Boolean object which can have custom props but being an object is always truthy. Modernizr returns either boolean false or a truthy Boolean object.

My original question assumed the trick works differently and so most popular answers deal with (perfectly valid) coding standards aspect. However I find the answers debunking the whole trick most helpful (and also the ultimate arguments against using the method) so I'm accepting one of them. Thanks to all the participants!


Asked January 11, 2017
Posted Under: Programming
46 views
8 Answers

Congratulations, you've discovered objects. The reason not to do this is called the principle of least astonishment. Being surprised by a design isn't a good thing.

There is nothing wrong with bundling together this information but why would you want to hide it in a Bool? Put it in something you'd expect to have all this info. Bool included.


Answered January 11, 2017
 
I edited the question. Here's to the principle of least astonishment. :-) – konrad yesterday
 CanDoerz  5 months ago
 
"Unconventional" doesn't seem like a strong argument to me. – Robert Harvey 2 days ago
 CanDoerz  5 months ago
 
A bit more about usage. If you stick to returning a boolean value, you can pass all the functions to list operations like any(), all(), filter() easily. It circumvents lack of strong typing or formal interfaces in JS at the cost of being a bit unconventional - which seems so far to be the main argument against it. – konrad 2 days ago
 CanDoerz  5 months ago
 
LOL yes I mentioned object as an obvious alternative in my question. Principle of least astonishment is a good point even though with JS weak typing objects are less surprising but still confusing and you need to check docs anyway. As for the usage Modernizr is a good example: you have a large suite of tests with uniform true/false outcome and only now and then you need to pass more info - maybe even the need for those came later so when it does you can either make breaking changes to the whole library creating mostly redundant wrappers around booleans or you enhance the bool. IMO not so dumb. – konrad 2 days ago
 CanDoerz  5 months ago

In addition to the general design principles, like single responsibility, and least surprise, there's a JavaScript-specific reason that it's not a good idea: there's a huge difference between a boolean and Boolean in JavaScript that prevents it from working in the general case.

boolean is a primitive type, not an object, and cannot have custom properties. Expressions like true.toString() work because behind the scenes, it turns into (new Boolean(true)).toString().

Boolean (with a capital B) is an object, but has very few good uses, and being used as a boolean is definitely not one of them. The reason for that is, that every Boolean is "true", regardless of its value, because all objects get converted to true in a boolean context. For example, try this:

var answer = new Boolean(false);
if (answer) {
  console.log("That was unexpected.");
}

So, in general, there's no way to add properties to a boolean in JavaScript that still lets it behave in a logical way. Modernizr can get away with it because the only add properties to "true" values, which sort of work how you would expect (i.e. they work in if statements). If video isn't supported at all, Modernizr.video will be an actual boolean (with the value false), and can't have properties added to it.


Answered January 11, 2017
 
@konrad For reference, by default JavaScript will pretend it's letting you add properties to primitives by not complaining when you try to do it. Using strict mode will fix that, and throw a TypeError if you try to add a property (see jsbin.com/yovasafibo/edit?js,console). – Matthew Crumley yesterday
 CanDoerz  5 months ago
 
@ArturoTorresSánchez that's why I added the qualifier "nominally" :P – Jared Smith yesterday
 CanDoerz  5 months ago
 
@ArturoTorresSánchez because the way they do it the return values are nominally of the same type. – Jared Smith yesterday
 CanDoerz  5 months ago
 
Thanks a lot for a sound technical argument. It seems like my quick test was flawed: jsbin.com/hurebi/3/edit?js,console. Even after adding custom props to false it's both strictly 'false' and falsy (=== and == work as ) BUT indeed you actually can't add props to primitive bool. The distinction between Bool and bool fleeted me apparently. – konrad yesterday
 CanDoerz  5 months ago
 
I find Modernizr's approach rather odd, considering that having just an object with properties already evaluates to true in a conditional. Why explicitly use a Boolean? – Arturo Torres Sánchez yesterday
 CanDoerz  5 months ago

The main argument I hold against it is, the single responsibility principle, a boolean should only say if something is true or false, not why or how or any other thing. It is my firm belief and practice that other objects should be used to communicate that or any other information.


Answered January 11, 2017
 
@Casey the problem is not that containing this information by itself is a violation of SRP. It only becomes a problem when combined with the responsibility that a boolean already has - to represent true or false. JavaScript's Boolean shenanigans notwithstanding. – Jacob Raihle yesterday
 CanDoerz  5 months ago
 
@Casey No since the purpose of that object would be different of the original boolean. And it would have only one responsibility, to communicate the state and reason of a transaction, as an example. – J. Pichardo yesterday
 CanDoerz  5 months ago
 
But if you have an object defining this and some other things doesn't it arguably violate the same principles? – Casey yesterday
 CanDoerz  5 months ago
 
do you mean boolean or Boolean (with capital B) in javascript there's a difference. – Pieter B yesterday
 CanDoerz  5 months ago

Since the whole reason it is called a boolean value is the fact it is true or false, I dislike the idea, as you pretty much undermine its whole purpose from wikipedia

In computer science, the Boolean data type is a data type, having two values (usually denoted true and false), intended to represent the truth values of logic and Boolean algebra.

(my bolding)


Answered January 11, 2017
 
If it can take a value other than true or false, it also isn't a boolean. Which, given the name, is silly. – Useless yesterday
 CanDoerz  5 months ago
 
I think the question is about the Boolean object not the boolean primitive. The Object is not a value. – Pieter B yesterday
 CanDoerz  5 months ago
 
if you are returning yes – svarog 2 days ago
 CanDoerz  5 months ago
 
@svarog the only necessity I can see is unskilled designer of the code. If you need to return bool and something else, make it a class, or tuple, or list, or anything else that fits in the particular code. – MatthewRock 2 days ago
 CanDoerz  5 months ago
 
correct, but necessity may dictate otherwise – svarog 2 days ago
 CanDoerz  5 months ago

Just because you can doesn't mean you should, in JS you can pretty much attach properties to any object (Booleans included)

How is that a bad thing?

On one hand, you can attach more irrelevant data to a Boolean (per your example), something that another developer will not expect because why should it be there?! bools are just for true and false.

A counter point for it is to attach some relevant useful properties that may help you deal with the boolean value (Java does that).

For example, you can attach a function that converts the boolean value into a string, a dirty flag that turned true if the value was changed, watchers and event callbacks that can fire when the value is changed etc..

but the returned Boolean (Bool is first class object in JS) has properties specifying what formats are supported

It sounds like something that shouldn't be stored in a Boolean, but rather using a Set of booleans, or a Set of properties with booleans inside them. I think a better approach would be to return an object with all the formats and support details.

{
    isSomethingSupported: true,
    isSomethingElseSupported: false,
    ....
}

Answered January 11, 2017
 
do you mean boolean or Boolean (with capital B) ? – Pieter B yesterday
 CanDoerz  5 months ago

You can't make booleans with custom properties in JavaScript. The following fail (at least in FF):

    var x = false;
    x.foo = "bar";
    console.log(x.foo);

You can use, or inherit from Boolean, but as Matthew Crumley says it gives different results. Boolean is of type Object. When JS needs the boolean value of an expression it converts using the specification function ToBoolean, which mandates that for Objects the result is always true. Thus the value new Boolean(false) evaluates to true! You can verify this in this example: https://jsfiddle.net/md7abx5z/3/ .

The only reason it works for Modernizr is incidental. They only create a Boolean object when the condition is true. When they evaluate false they just return ordinary false. So it works because they only return Boolean objects when the result was true anyway, and never when it is false.


Answered January 11, 2017

If I look at the code this isn't really about giving Boolean custom properties but about a method having return methods with different signatures.

If it's false then you get a primitive false and if it's true it returns an object which by definition gets interpreted as true in javascript.

So, in my opinion your question shouldn't be whether it's a good idea to give Boolean custom properties but whether it's a good idea to have methods with multiple return signatures.


Answered January 11, 2017

In the given example, couldn't you just return an array of all the supported video formats?

  • An empty array means "no video formats supported", which in order means "no video supported".
  • Otherwise, if any video format is supported, then obviously video in general is supported.

I would at least say that using array is somewhat less surprising than having "custom booleans".

In Javascript, an empty array is even considered falsy, while a non-empty array is truthy, so with some luck you could just switch to arrays and everything would be working just as before edit Nope, silly me for thinking I could remember the truthiness of objects in JavaScript :P


Answered January 11, 2017
 
@daniero The point about typeof [] === 'object' is that any Object is truthy, even new Boolean(false). Some primitive types (e.g. string "" and number 0) are falsy, but they are not Objects as far as typeof is concerned. It's just another way to remember. It's not about proving anything. The proof is in the specification or the tests, whichever you prefer. – joshp yesterday
 CanDoerz  5 months ago
 
On the other hand, [].length is falsey if length is 0, is not much more typing, and in my opinion is a better indication of intent than just if([]) would be even if that worked. – IllusiveBrian yesterday
 CanDoerz  5 months ago
 
Although, I'm not sure that the typeof thing proves anything; "" has typeof "string" but it is actually falsy – daniero yesterday
 CanDoerz  5 months ago
 
@joshp oops, you're right, my bad. Fixed – daniero yesterday
 CanDoerz  5 months ago
 
An empty array does not evaluate as false in an if statement. if ([]) console.log('not false'); prints 'not false' in Chrome for example. typeof [] === 'object' so no an empty array is not falsy. See developer.mozilla.org/en-US/docs/Glossary/Truthy for reference. – joshp yesterday
 CanDoerz  5 months ago
Your Answer
D:\Adnan\Candoerz\CandoProject\vQA