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)
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.
Sorry, but then you just get a lot of enums
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.
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:
Did I read you right, Ben? (@moopet )
But if you say booleans cannot be optional, then the consequence is that there cannot be default values for boolean properties.
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.
@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?
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.
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."
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"
!In JS, if no value was provided, the type will be the string
"undefined"
, and if a value is provided but isn'ttrue
orfalse
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.
Consider this more modern TS example:
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.
If you use TypeScript probably also with runtypes, it's easy to do
'x' | 'y' | 'z'
, and all aretruthy
.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.