41

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!

  • 35
    Extending the boolean type is a classical wtf. – hlovdal Jan 9 '17 at 15:18
  • 7
    Note that in JS, they could have just returned null if not supported and an array of formats if so. A list is considered truthy in JS, and null is falsy. – jpmc26 Jan 9 '17 at 16:32
  • 1
    Related question: My value checker function needs to return both a boolean and a message – user22815 Jan 9 '17 at 16:38
  • 3
    Before you answer this question: in javascript there's a big difference between "boolean" and "Boolean". They are not the same thing and any answer which doesn't capitalize Boolean is invalid. – Pieter B Jan 9 '17 at 17:44
  • 2
    For those of you who are stunned to see something like this in the wild, in a library as popular as Modernizr, here is the relevant code from their GitHub: github.com/Modernizr/Modernizr/blob/… – Chris Nielsen Jan 9 '17 at 20:29
38

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.

  • 18
    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 Jan 9 '17 at 18:29
  • 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 Jan 9 '17 at 20:08
  • @ArturoTorresSánchez because the way they do it the return values are nominally of the same type. – Jared Smith Jan 9 '17 at 21:07
  • 1
    @ArturoTorresSánchez that's why I added the qualifier "nominally" :P – Jared Smith Jan 9 '17 at 21:29
  • 1
    @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 Jan 9 '17 at 22:30
59

Congratulations, you've discovered objects. The reason not to do this is called the principle of least astonishment. Being surprised by a design is not 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.

  • 1
    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 Jan 9 '17 at 9:51
  • 1
    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 Jan 9 '17 at 10:09
  • 1
    "Unconventional" doesn't seem like a strong argument to me. – Robert Harvey Jan 9 '17 at 17:02
  • I edited the question. Here's to the principle of least astonishment. :-) – konrad Jan 9 '17 at 20:34
16

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.

  • do you mean boolean or Boolean (with capital B) in javascript there's a difference. – Pieter B Jan 9 '17 at 17:46
  • But if you have an object defining this and some other things doesn't it arguably violate the same principles? – Casey Jan 9 '17 at 18:40
  • 1
    @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 Jan 9 '17 at 19:15
  • 1
    @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 Jan 10 '17 at 15:49
10

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)

  • 1
    correct, but necessity may dictate otherwise – svarog Jan 9 '17 at 14:07
  • 8
    @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 Jan 9 '17 at 15:00
  • if you are returning yes – svarog Jan 9 '17 at 15:09
  • I think the question is about the Boolean object not the boolean primitive. The Object is not a value. – Pieter B Jan 9 '17 at 17:45
  • 1
    If it can take a value other than true or false, it also isn't a boolean. Which, given the name, is silly. – Useless Jan 9 '17 at 18:21
2

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,
    ....
}
  • 1
    do you mean boolean or Boolean (with capital B) ? – Pieter B Jan 9 '17 at 17:46
1

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.

1

I understand the context has changed, but I'd like to answer the original question which I read as using JS as an example, but not limited to just JS.

Adding properties to a boolean, would not be a problem IF the properties had something to do with the true/false, not with variable holding the value. For instance, adding a toYesNoString method would be fine, adding a numberOfChildren to a hasChildren value isn't, and neither would questionsMissed studentPassed. There's not a lot you could add to a boolean, other than various string repesentations, the only property that I can think of that would make sense is originalExpression. But adding to it isn't necessarily a bad idea in theory.

0

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.

0

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

  • 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 Jan 9 '17 at 18:00
  • @joshp oops, you're right, my bad. Fixed – daniero Jan 9 '17 at 18:25
  • Although, I'm not sure that the typeof thing proves anything; "" has typeof "string" but it is actually falsy – daniero Jan 9 '17 at 20:41
  • 1
    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 Jan 9 '17 at 21:26
  • @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 Jan 9 '17 at 22:52

protected by Community Jan 9 '17 at 17:39

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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