DEV Community

Alex Lohr
Alex Lohr

Posted on

Optional booleans?

If you define APIs, you usually discuss names and types a lot. One of these discussions brought up that one of my colleagues' dislike of optional boolean properties - and there's also a good reason for it, because undefined and false are coerced to the same value, which can lead to confusion.

I personally value the developer experience of having sensible defaults whenever possible over the possible misunderstanding of false and undefined.

Any other advantages and disadvantages I have overlooked? Please discuss!

Top comments (10)

Collapse
 
moopet profile image
Ben Sinclair

If something can have three states, it shouldn't be a boolean. If it needs to have one of two states, then existing data should be updated with the default (whether that's true or false).

"Optional booleans" only really pop up when boolean has been added to a schema, but the developers haven't made it required because they don't want to break backwards-compatibility. They should set the field to the default on all existing records.

Collapse
 
lexlohr profile image
Alex Lohr • Edited

Sorry, but then you just get a lot of enums

enum Whatever {
  Default,
  On,
  Off
}

I don't think that's an improvement, rather the contrary.

Especially for a web/react/Vue/etc component, that makes no sense at all.

Collapse
 
daniel13rady profile image
Daniel Brady

Hmm, I interpreted Ben's comment about 'defaults' as "always have one, and when adding one retroactively be sure to backfill existing data," not "have a 'default' value that is distinct from all others in the type". This seems to align with and extend your own comment:

I personally value the developer experience of having sensible defaults whenever possible

Did I read you right, Ben? (@moopet )

Thread Thread
 
lexlohr profile image
Alex Lohr

But if you say booleans cannot be optional, then the consequence is that there cannot be default values for boolean properties.

Thread Thread
 
daniel13rady profile image
Daniel Brady

I think that makes sense, though, right? If it's required, a default isn't necessary or applicable. If it's optional, you need a sane default.

But I didn't say anything about not having optional booleans: quite the contrary, I was talking about the case where you do have optional booleans, and paraphrasing Ben's comment about it.

Thread Thread
 
moopet profile image
Ben Sinclair

@dabrady yeah, that's what I meant. If there's a useful semantic difference between three states then it should be an enum or something, but if the third state is just there beacuse it's unfilled, why not fill it?

Thread Thread
 
lexlohr profile image
Alex Lohr

Because it might only be for one of the use cases of your API. Should the developer using it need to care about all the use cases that don't matter to him? I should think that is a waste of his time.

Collapse
 
daniel13rady profile image
Daniel Brady • Edited

Optional booleans in APIs make sense to me in the same way that optionals in general make sense: they aren't optional to the API implementation, they're optional to the API consumer.

It's important to remember an optional boolean is not a boolean, it is a union type of boolean and whatever the type is for "value absence".

For many languages, this is tricky to work with, but Typescript and JavaScript make it easy.

A lot of people will rely on the simplicity of type coercion for implementing presence checks: if the value is "falsey", it's considered "absent."

function foo(x) {
  if (x) {
    alert("x is present");
  } else {
    alert("x is absent");
  }
}

But this of course breaks when the value is false exactly!

Instead, we should remember that in JS variables don't have types, only values do, and so doing a "presence check" on something that is supposed to be a boolean is as simple as asking typeof x == "boolean"!

function foo(x) {
  if (typeof x == "boolean") {
    alert("x is present");
  } else {
    alert("x is absent or not a boolean");
  }
}

In JS, if no value was provided, the type will be the string "undefined", and if a value is provided but isn't true or false the type will be something else.

This is amazing because it avoids the bugs that can happen when you try to determine presence based on the value itself.

Collapse
 
lexlohr profile image
Alex Lohr

Consider this more modern TS example:

const whatever = ({ darkMode = false, ...rest }) => null

It is not three different values, but a boolean value with a default so it can be optional.

If this was a component, every user would be forced to declare the darkMode property like it or not if it was not optional, which to me makes no sense if you can have a default.

Collapse
 
patarapolw profile image
Pacharapol Withayasakpunt

If you use TypeScript probably also with runtypes, it's easy to do 'x' | 'y' | 'z', and all are truthy.

I believe API server should validate the input anyway (actually client-side as well), probably with JSON Schema and AJV.

But it might can be done with runtypes as well.